Une macro est un nom que le préprocesseur remplace par du texte avant que le compilateur ne tourne. C'est tout le mécanisme, et chaque règle de cette page en découle : les macros n'ont pas de type, ne respectent pas la portée, n'évaluent pas leurs arguments, et ignorent ce qu'est un appel de fonction. Elles copient du texte.
Utilisées avec soin, elles sont indispensables - interrupteurs de portabilité, macros d'assertion qui capturent le numéro de ligne, constantes de compilation utilisables dans des tailles de tableau. Utilisées à la légère, elles produisent des bugs qui semblent impossibles tant que vous n'avez pas développé le fichier.
Les macros objets
La forme la plus simple définit un nom qui représente une valeur :
Conventionnellement, ces noms sont en MAJUSCULES_AVEC_TIRETS_BAS pour qu'un lecteur voie d'un coup d'œil qu'un symbole est une macro et non une variable. Cette convention compte plus qu'ailleurs ici, car les macros ignorent la portée : un #define dans une fonction s'applique quand même au reste du fichier, et il réécrira volontiers une variable du même nom dans une autre fonction.
#undef NOM supprime une définition, de sorte qu'un nom peut être redéfini plus tard sans avertissement de redéfinition.
Les macros à la fonction
Mettez une liste de paramètres directement après le nom - sans espace avant la parenthèse ouvrante - et la macro prend des arguments :
La règle de l'espace est réelle : #define SQUARE (x) ((x)*(x)) définit une macro objet nommée SQUARE dont le texte de remplacement commence par (x). L'erreur de compilation qui suivra ne mentionnera pas les espaces.
Les règles de parenthèses
Il y en a deux, et toutes deux existent parce que le corps est collé dans une expression que vous ne voyez pas en écrivant la macro.
Règle 1 : mettez chaque paramètre entre parenthèses. Sans cela, un argument qui est lui-même une expression est déchiré par la précédence :
#define SQUARE_BAD(x) x * x
SQUARE_BAD(2 + 3) /* devient 2 + 3 * 2 + 3 == 11, pas 25 */
Règle 2 : mettez tout le corps entre parenthèses. Sans cela, c'est l'expression environnante qui déchire le résultat :
#define DOUBLE_BAD(x) (x) + (x)
10 / DOUBLE_BAD(5) /* devient 10 / (5) + (5) == 7, pas 1 */
Faites tourner les deux échecs côte à côte avec leurs versions corrigées :
Les deux mauvaises versions compilent sans le moindre avertissement et produisent de mauvais nombres. L'habitude à prendre est mécanique : entourez chaque paramètre, puis entourez le résultat. ((a) > (b) ? (a) : (b)) a l'air bruyant, et ce bruit est le but.
Le piège de l'évaluation multiple
Les parenthèses ne peuvent pas vous sauver du second danger. Une macro colle le texte de son argument à chaque endroit où le paramètre apparaît, donc un argument à effet de bord se produit plus d'une fois :
La macro se développe en ((i++) > (j) ? (i++) : (j)). La comparaison incrémente i à 6 et compare 5 avec 3 ; la branche vraie incrémente i encore une fois à 7 et donne 6. Donc m vaut 6 et i vaut 7 - ni l'un ni l'autre n'est ce que « le plus grand de i et j » devrait donner. Une fonction max(i++, j) serait correcte, car l'argument est évalué une fois avant l'appel.
Le même piège attrape tout ce qui a un effet de bord : SQUARE(rand()) appelle rand() deux fois et multiplie deux nombres différents. CHECK(read_byte()) consomme deux octets.
Deux défenses, par ordre de préférence :
- Utilisez une vraie fonction. S'il vous en faut pour plusieurs types, écrivez-en une par type, ou utilisez une fonction
static inlinedans un en-tête. - Si ce doit être une macro, documentez bruyamment que les arguments sont évalués plus d'une fois, et gardez les points d'appel exempts d'effets de bord.
Macros multilignes et do-while(0)
Un corps de macro peut s'étendre sur plusieurs lignes si chaque ligne sauf la dernière se termine par une barre oblique inverse. La version naïve semble correcte :
#define LOG_TWICE(msg) \
printf("%s\n", msg); \
printf("%s\n", msg)
Puis elle casse, silencieusement, au seul endroit qui compte :
if (verbose)
LOG_TWICE("bonjour");
else
printf("silence\n");
Après substitution, le if ne possède que le premier printf, le second s'exécute sans condition, et le else n'a plus de if correspondant - une erreur de compilation dont le message ne pointe nulle part d'utile. Entourer le corps de simples accolades ne vaut pas mieux : le ; final après LOG_TWICE("bonjour") devient une instruction vide qui termine le if, et le else casse de nouveau.
L'idiome qui fonctionne est do { ... } while (0) :
do { ... } while (0) est une instruction unique, elle exécute son corps exactement une fois, et elle exige un point-virgule après elle - de sorte que le point d'appel se lit comme un appel de fonction ordinaire et se comporte comme tel dans tout contexte de flux de contrôle. Attention aux barres obliques inverses : un espace égaré après l'une d'elles termine la continuation et produit une erreur déroutante.
Macros prédéfinies et motifs d'assertion
Le préprocesseur définit lui-même plusieurs macros, et elles expliquent pourquoi certaines choses ne peuvent tout simplement pas être des fonctions :
__FILE__ et __LINE__ se développent là où ils sont écrits, donc les placer dans une macro capture la position de l'appelant. Une fonction ne pourrait pas faire cela - à l'intérieur d'une fonction, ils rapporteraient toujours le fichier et la ligne de la fonction de journalisation elle-même. C'est exactement ainsi que la macro standard assert rapporte l'emplacement de l'expression en échec.
Autres noms prédéfinis utiles : __DATE__, __TIME__ et __STDC_VERSION__ (par exemple 201710L pour C17).
Deux opérateurs à connaître
Dans un corps de macro, # transforme un paramètre en littéral de chaîne (« stringification ») et ## colle des jetons ensemble (« concaténation de jetons »).
SHOW(width * height) devient printf("width * height" " = %d\n", (width * height)); - les littéraux de chaîne adjacents sont joints par le compilateur, donc un seul appel affiche à la fois le texte de l'expression et sa valeur. C'est une astuce de débogage à retenir.
## est plus rare et apparaît surtout dans les macros de génération de code : #define MAKE_VAR(n) int var_##n transforme MAKE_VAR(3) en int var_3. Usez-en avec parcimonie ; les identifiants construits par le préprocesseur ne peuvent pas être recherchés par leur nom, ce qui rend le code difficile à parcourir.
Macros contre fonctions contre const
Utilisez l'outil le plus faible qui fait le travail :
- Une variable
const-const double PI = 3.14159;a un type, obéit à la portée, apparaît dans le débogueur, et ne peut pas être redéfinie par un en-tête sans rapport. À préférer pour les valeurs utilisées à l'exécution. enum-enum { MAX_USERS = 100 };donne une constante entière nommée qui fonctionne là où le C exige une constante de compilation, avec un type et une portée. Bon pour les limites entières.- Une fonction
static inline- vérifiée au niveau des types, évalue chaque argument exactement une fois, et les compilateurs modernes l'inlinent tout comme une macro. C'est le bon remplacement de presque toutes les macros à la fonction. - Une macro - quand vous avez besoin de
__LINE__, quand la chose doit fonctionner avant que les types n'existent (tailles de tableaux dans du vieux code), quand vous basculez du code avec la compilation conditionnelle, ou quand vous générez du code répétitif.
Un domaine où les macros restent incontestées est le dimensionnement de tableau, car le résultat doit être une constante de compilation :
Celle-ci a sa propre réserve bien connue : elle n'est correcte que pour un véritable tableau. Passez un pointeur - ce que devient un tableau confié à une fonction - et sizeof mesure le pointeur, donnant une réponse fausse et parfaitement silencieuse.
Déboguer une macro
Quand une macro se comporte mal, ne la fixez pas du regard. Développez-la :
gcc -E program.c | tail -30
Le texte substitué vous dit immédiatement si le problème est une parenthèse manquante, une double évaluation, ou un nom en collision avec autre chose. Compilez aussi avec -Wall -Wextra - GCC et clang ajoutent des notes « in expansion of macro » qui relient la ligne d'erreur signalée à la définition.
Ensuite : les fichiers d'en-tête, où #define et #include se combinent pour laisser un projet s'étendre sur de nombreux fichiers sources sans coller deux fois les mêmes déclarations.
Questions fréquentes
Qu'est-ce qu'une macro en C ?
Un morceau de texte nommé que le préprocesseur substitue dans votre source avant la compilation. #define MAX 100 fait que chaque MAX ultérieur devient 100 ; #define SQUARE(x) ((x) * (x)) prend des arguments et les substitue dans un motif. Les macros n'ont pas de type et n'obéissent à aucune règle de portée - c'est du remplacement de texte.
Pourquoi les macros C ont-elles besoin d'autant de parenthèses ?
Parce que le corps est collé dans le code environnant puis analysé dans son ensemble. #define SQUARE(x) x * x transforme SQUARE(2 + 3) en 2 + 3 * 2 + 3, soit 11 au lieu de 25. Entourer chaque argument et tout le corps - ((x) * (x)) - fait que le texte substitué se groupe comme vous l'entendiez, quoi qu'il y ait autour.
Quelle est la différence entre une macro et une fonction en C ?
Une fonction est compilée une fois, vérifiée au niveau des types, et évalue chaque argument exactement une fois. Une macro est collée à chaque usage, ne vérifie rien, et peut évaluer un argument plusieurs fois - donc MAX(i++, j) peut incrémenter i deux fois. Préférez les fonctions (et const/enum pour les constantes) ; utilisez les macros pour ce que les fonctions ne peuvent pas faire, comme capturer __LINE__ ou générer du code.
Pourquoi une macro multiligne est-elle entourée de do { ... } while (0) ?
Pour qu'elle se comporte comme une instruction unique. Un corps en simples { ... } casse quand un point-virgule le suit dans un if/else (le point-virgule termine le if trop tôt), et une simple suite d'instructions casse dans un if sans accolades. do { ... } while (0) est une instruction unique qui accepte un point-virgule final et exécute son corps exactement une fois.