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 :
provoque le remplacement suivant :
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 :
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 :
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 :
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 :
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.