Makra preprocesora w C powstały jako część języka, aby zapewnić przenośność, czytelność i łatwość dostosowania kodu źródłowego. Dzięki dyrektywom #define, #ifdef, #ifndef można tworzyć warunkowe sekcje kodu, deklarować stałe oraz dołączać zewnętrzne pliki (#include).
Historia pytania:
Dyrektywy preprocesora zostały wprowadzone, aby uprościć adaptację kodu źródłowego do różnych systemów i kompilatorów, a także zautomatyzować powtarzające się czynności.
Problem:
Bez preprocesora nie można było abstrahować od różnic platformowych, powtarzać fragmenty kodu, ani bronić się przed podwójnym dołączaniem plików nagłówkowych. Ponadto, trzeba być ostrożnym, ponieważ makra nie są typizowane i nie mają zakresu widoczności.
Rozwiązanie:
Używanie makr preprocesora do deklaracji stałych, funkcji inline, kompilacji warunkowej i zapobiegania wielokrotnemu dołączaniu plików nagłówkowych.
Przykład kodu:
#ifndef MY_HEADER_H #define MY_HEADER_H #define MAX_SIZE 100 #ifdef DEBUG #define LOG(x) printf("%s\n", x) #else #define LOG(x) #endif #endif /* MY_HEADER_H */
Kluczowe cechy:
Co się stanie, jeśli zapomnisz o include guard w pliku nagłówkowym?
Plik nagłówkowy może być dołączony do jednego pliku .c kilka razy (niejawnie przez inne .h), co doprowadzi do błędów redefinicji.
Czym różni się makro #define od deklaracji funkcji inline?
#define po prostu zastępuje tekst, nie sprawdzając typów ani poprawności składni, podczas gdy funkcja inline to zwykła funkcja, która jest sprawdzana przez kompilator na etapie analizy typów.
Czy makra mogą zawierać efekty uboczne?
Tak, jeśli makro jest źle zdefiniowane, to przekazywane wyrażenia mogą być obliczane kilka razy. Na przykład:
#define SQUARE(x) (x) * (x) int a = SQUARE(++i); // ++i zostanie wykonane DWUKROTNIE!
Użycie makra bez nawiasów i z wyrażeniem, które tworzy efekty uboczne:
#define DOUBLE(x) x + x int result = DOUBLE(1+2); // wynik nie 6, ale 1+2+1+2=6? Nie, więc 1+2+1+2=6 — tak? Nie, okazuje się 1+2+1+2. // Ale w rzeczywistości będzie to 1 + 2 + 1 + 2 = 6, ale jeśli DOUBLE(++i), to ++i zostanie zastosowane dwukrotnie.
Zalety:
Wady:
Definiowanie makra z nawiasami i używanie go do prostych stałych:
#define DOUBLE(x) ((x) + (x)) #define BUFFER_SIZE 1024
Zalety:
Wady: