Mam problem, czytałem książkę jak i przeglądałem wiele stron. Nie wiem nie jest to dla mnie nadal jasne. W pliku nagłówkowym z rozszerzeniem .h powinny się znaleźć deklaracje zmiennych i funkcji oraz wszystkie dyrektywy które są nam potrzebne a w źródłowym wszystkie definicje (tzn. cało funkcji) ? Jak użyć przy tym operatora "extern" aby pobrać deklaracje zmiennej z innego modułu, skoro dołączamy plik nagłówkowy do pliku żródłowego to ma już przecież wszystko jest. Na jak to dokładnie działa ?
Generalnie odradzam zmiennych globalnych. Ale jeśli już na prawdę masz uzasadnione użycie, to wtedy w pliku .h robisz extern
, a w jakimś jednym pliku .c robisz definicje tej zmiennej, np.
.h:
extern int g_variable;
.c:
int g_variable;
Co umieszczać w nagłówkach?
- możliwie jak najmniej includeów własnych nagłówków
- symbole preprocesora
-
forward declarations
i prototypy funkcji - definicje struktur i enumów, które są współdzielone między wieloma plikami .c
- funkcje
inline
- wszelkie stałe
Frreq napisał(a):
Jak użyć przy tym operatora "extern" aby pobrać deklaracje zmiennej z innego modułu, skoro dołączamy plik nagłówkowy do pliku żródłowego to ma już przecież wszystko jest. Na jak to dokładnie działa ?
Gdybyś w pliku nagłówkowym nie używał extern
to znaczy że definiujesz zmienną w pliku nagłówkowym. Potem jeśli 2 pliki .c
dołączają ten plik nagłówkowy to każdy z nich będzie miał swoją kopię tej zmiennej. Co prowadzi do błędu linkera, bo istnieją 2 zmienne globalne o tej samej nazwie.
Teraz użycie extern
w pliku nagłówkowym jedynie deklaruje taką zmienną, czyli mówi "ta zmienna gdzieś istnieje, ja się tym nie przejmuję ale linker będzie wiedział". Teraz oczywiście jeden z plików .c
musi definiować taką zmienną, inaczej będzie błąd bo próbujesz używać zmiennej, która nie istnieje. Wszystkie inne pliki .c
które dołączają nagłówek z extern
będą używać właśnej tej jednej zmiennej.
Podobna sytuacja z deklaracją/definicją jest z funkcjami, ale różnica jest taka, że deklaracja funkcji nie potrzebuje słowa extern
, po prostu brak ciała oznacza że to jest deklaracja.
Dzięki wielkie, rozjaśniło mi to trochę pogląd na podział programu na pliki i używanie głównie operatora extern. Poszukam jeszcze więcej informacji na ten temat jak to zoptymalizować.
Czyli jeżeli dobrze zrozumiałem powinno to wyglądać mniej więcej tak :
plik main.c
#include "Head.h"
#include "dodawanie.c"
#include "odejmowanie.c"
#include "mnozenie.c"
int main()
{
int number1;
int number2;
int hnumber = 0;
int option = 0;
while(1)
{
printf("XXXXXXXX WITAJ W KALKULATORZE XXXXXXXX\n\n");
printf("wybierz opcje : \n\n");
printf("1. DODAWANIE\n");
printf("2. ODEJMOWANIE\n");
printf("3. MNOZENIE\n");
printf("4. POTENGOWANIE\n");
scanf("%d",&option);
system("CLS");
switch(option)
{
case 1: printf("DODAWANIE");
system("CLS");
printf("Podaj wartosc a : ");
scanf("%d",&number1);
printf("\nPodaj wartosc b : ");
scanf("%d",&number2);
hnumber = dodawanie(number1,number2);
printf("Wynik = %d",hnumber);
getch();
system("CLS");
break;
case 2: printf("ODEJMOWANIE");
system("CLS");
printf("Podaj wartosc a : ");
scanf("%d",&number1);
printf("\nPodaj wartosc b : ");
scanf("%d",&number2);
hnumber = odejmowanie(number1,number2);
printf("Wynik odejmowania = %d", hnumber);
getch();
system("CLS");
break;
/*case 3: printf("MNOZENIE");
system("CLS");
mnozenie();
break;
case 4: printf("POTEGOWANIE");
system("CLS");
potengowanie();
break; */
default : printf("Wybrales nie prawidolowa wartosc");
printf("Wcisnij dowolny klawisz aby wrocic");
getch();
system("CLS");
break;
}
}
return 0;
}
Plik Head.h
#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
extern int number1,number2,hnumber,suma,option;
int dodawanie();
int odejmowanie();
float mnozenie();
float potengowanie();
#endif // HEAD_H_INCLUDED
plik dodawanie.c
#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED
int dodawanie(int number1, int number2)
{
int suma = 0;
suma = number1+number2;
return suma;
}
#endif
itd.
Czy można coś zoptymalizować w kodzie ? Miał to być prosty kalkulator
Po pierwsze się nie dołącza plików .c
#include "dodawanie.c"
To jest ogólna zasada. Z tego powodu plik .c
nie powinien mieć strażników dołączania (autorskie tłumaczenie).
Natomiast w tym przypadku wystarczy, że dołączasz plik nagłówkowy i on ma tam deklaracje tych zmiennych globalnych.
Po drugie nie używaj zmiennych globalnych to nie będziesz musiał się bawić extern
ami. Zmienne globalne są złe z wielu powodów, więc unikanie ich to dobra praktyka tak czy siak.
Po trzecie ten konkretny przykład jest na tyle banalny, że nie ma sensu go rozdzielić na osobne pliki.
Przykład wybrałem taki, ponieważ chciałem sprawdzić działanie. Lubie sprawdzić jak coś działa na przykładnie, łatwiej jest mi zapamiętać później jak wykorzystać dany element.
To już zrozumiałem, kompilator w takim razie dołącza wszystkie pliki źródłowe z automatu, a plik nagłówkowy w takim razie należy dołączyć ręcznie do każdego z plików źródłowych czy tylko to pliku głównego ?
Kompilator nie dołącza plików źródłowych, tylko je kompiluje, i to też nie automagicznie ale dlatego że masz te pliki dołączone do projektu.
A plik nagłówkowy dołączasz wtedy gdy potrzebujesz. Czyli gdy potrzebujesz którejś deklaracji w tym pliku. Inaczej mówiąc mógłbyś się obejść zupełnie bez pliku nagłówkowego, wystarczy że masz deklaracje rzeczy których używasz. Ale jeśli w kilku plikach .c
potrzebujesz tych samych deklaracji to wygodniej jest je wrzucić do pliku nagłówkowego i zrobić jeden #include
.
Zarejestruj się i dołącz do największej społeczności programistów w Polsce.
Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.