5.3. Les macros

Le préprocesseur peut, lors du mécanisme de remplacement de texte, utiliser des paramètres fournis à l'identificateur à remplacer. Ces paramètres sont alors replacés sans modification dans le texte de remplacement. Le texte de remplacement est alors appelé macro.

La syntaxe des macros est la suivante :

#define macro(paramètre[, paramètre [...]]) définition

Exemple 5-2. Macros MIN et MAX

#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))

Note : Pour poursuivre une définition sur la ligne suivante, terminez la ligne courante par le signe '\'.

Le mécanisme des macros permet de faire l'équivalent de fonctions générales, qui fonctionnent pour tous les types. Ainsi, la macro MAX renvoie le maximum de ses deux paramètres, qu'ils soient entiers, longs ou réels. Cependant, on prendra garde au fait que les paramètres passés à une macro sont évalués par celle-ci à chaque fois qu'ils sont utilisés dans la définition de la macro. Cela peut poser des problèmes de performances ou, pire, provoquer des effets de bords indésirables. Par exemple, l'utilisation suivante de la macro MIN :

MIN(f(3), 5)

provoque le remplacement suivant :

((f(3))<(5))?(f(3)):(5))

soit deux appels de la fonction f si f(3) est inférieur à 5, et un seul appel sinon. Si la fonction f ainsi appelée modifie des variables globales, le résultat de la macro ne sera certainement pas celui attendu, puisque le nombre d'appels est variable pour une même expression. On évitera donc, autant que faire se peut, d'utiliser des expressions ayant des effets de bords en paramètres d'une macro. Les écritures du type :

MIN(++i, j)

sont donc à prohiber.

On mettra toujours des parenthèses autour des paramètres de la macro. En effet, ces paramètres peuvent être des expressions composées, qui doivent être calculées complètement avant d'être utilisées dans la macro. Les parenthèses forcent ce calcul. Si on ne les met pas, les règles de priorités peuvent générer une erreur de logique dans la macro elle-même. De même, on entourera de parenthèses les macros renvoyant une valeur, afin de forcer leur évaluation complète avant toute utilisation dans une autre expression. Par exemple :

#define mul(x,y) x*y

est une macro fausse. La ligne :
mul(2+3,5+9)
sera remplacée par :
2+3*5+9
ce qui vaut 26, et non pas 70 comme on l'aurait attendu. La bonne macro est :
#define mul(x,y) ((x)*(y))
car elle donne le texte suivant :
((2+3)*(5+9))
et le résultat est correct. De même, la macro :

#define add(x,y) (x)+(y)

est fausse, car l'expression suivante :
add(2,3)*5
est remplacée textuellement par :
(2)+(3)*5
dont le résultat est 17 et non 25 comme on l'aurait espéré. Cette macro doit donc se déclarer comme suit :
#define add(x,y) ((x)+(y))

Ainsi, les parenthèses assurent un comportement cohérent de la macro. Comme on le voit, les parenthèses peuvent alourdir les définitions des macros, mais elles sont absolument nécessaires.

Le résultat du remplacement d'une macro par sa définition est, lui aussi, soumis au préprocesseur. Par conséquent, une macro peut utiliser une autre macro ou une constante définie avec #define. Cependant, ce mécanisme est limité aux macros qui n'ont pas encore été remplacées afin d'éviter une récursion infinie du préprocesseur. Par exemple :

#define toto(x) toto((x)+1)

définit la macro toto. Si plus loin on utilise « toto(3) », le texte de remplacement final sera « toto((3)+1) » et non pas l'expression infinie « (...(((3)+1)+1...)+1 ».

Le préprocesseur définit automatiquement la macro defined, qui permet de tester si un identificateur est connu du préprocesseur. Sa syntaxe est la suivante :

defined(identificateur)

La valeur de cette macro est 1 si l'identificateur existe, 0 sinon. Elle est utilisée principalement avec la directive #if. Il est donc équivalent d'écrire :

#if defined(identificateur)
   ⋮
#endif
et :
#ifdef identificateur
   ⋮
#endif

Cependant, defined permet l'écriture d'expressions plus complexes que la directive #if.