Historia pytania
Makra z parametrami są ważną częścią preprocesora C, powstały w celu szybkiego wdrażania powtarzających się fragmentów kodu i uproszczenia debugowania. Stosuje się je do małych funkcji, inline'owania lub optymalizacji.
Problem
Makra nie sprawdzają typów i nie wykonują pełnej substytucji poza prostym zastąpieniem tekstowym. Błędy występują z powodu braku nawiasów i zastępowania wyrażeń z efektami ubocznymi.
Rozwiązanie
Należy przewidywać nawiasy wokół parametrów i definicji makr, unikać efektów ubocznych w argumentach i korzystać z funkcji inline w bardziej skomplikowanych przypadkach.
Przykład kodu:
#define MAX(a, b) ((a) > (b) ? (a) : (b)) int x = 5, y = 10; int z = MAX(x++, y++); // Niebezpieczne wywołanie!
Kluczowe cechy:
Czy makro zawsze całkowicie zastępuje kod jak funkcja?
Nie! Makro to tylko zastąpienie tekstowe przed kompilacją, może zachowywać się inaczej niż funkcja, jeśli argumenty to wyrażenia z efektami ubocznymi.
Czy można używać dowolnego wywołania (w tym z ++, --) jako parametru makra?
To bardzo niebezpieczne. Efekty uboczne wystąpią kilka razy, jeśli parametr pojawi się w makrze więcej niż raz.
Przykład kodu:
// To wywołanie zwiększy x lub y więcej niż o 1 MAX(x++, y++)
Jak poprawnie umieszczać nawiasy w deklaracjach makr?
Obliczanie i parametrów, i wyrażenia wewnątrz makra nawiasami, aby uniknąć błędów dotyczących asocjatywności podczas wywołania wewnątrz innych wyrażeń.
W firmie przez wiele lat używano makra #define SQUARE(x) xx, stosując je w wyrażeniach typu SQUARE(a+1). Pojawiały się niespodziewane błędy: wyrażenie rozwijało się jako a+1a+1, co różni się od (a+1)*(a+1).
Zalety:
Makro SQUARE zostało napisane z pełnymi nawiasami: #define SQUARE(x) ((x)*(x)). Jego użycie zostało ustandaryzowane i udokumentowane.
Zalety: