Franck Butelle
LIPN, Université Paris 13 Formation Ingénieurs SupGalilée Info 3
11/2019
Qu’est-ce qu’une macro ?
Une macro c’est un bout de code auquel on a donné un nom ! Si on utilise le nom alors il est remplacé par le bout de code.
Traité par le pré-processeur avant la compilation par recherche/substitution comme dans un éditeur de texte ! Le pré-processeur lit le code dans l’ordre des lignes Le pré-processeur ne touche pas aux chaines de car.
Deux types de macros : les "constantes" et des pseudo-fonctions
#defineTAILLE _SIZE
#define_SIZE 1024
#definef(m,n) ((m)∗_SIZE +(n))
#defineNBRES 1,\
3
int X[ ] = { NBRES }, Y[TAILLE]; /∗ TAILLE vaut 1024 ∗/
#undef_SIZE
#define_SIZE 512
int Z[TAILLE]; /∗ TAILLE vaut 512 ∗/
Une macro= un bout de code !
#defineDEBUT { printf ("Debut !\n");
#defineFIN printf ("Fin !\n"); }
#defineAFFECT(m,n) n=m
#definePRINTINT(m) printf ("%d\n",m) int main (int argc, char ∗argv[ ]) DEBUT
int i ; AFFECT(3,i);
PRINTINT(i);
FIN
Debut ! 3
Fin !
Déboguer ses macros
Affiche à l’écran le résultat de la précompilation (y compris les inclusions...)
gcc -E testMacros.c
...
#12 "testMacros.c"
int main (int argc, char ∗argv[ ]) { printf ("Debut !\n");
int i ;
i=3;printf ("%d\n",i);
printf ("Fin !\n");}
Inconvénients des macros en C
Attention aux parenthèses !
perturbe la notion de numéro de ligne pour debug en exec
Ï correction partielle par ajout par le précomp de #num_de_ligne
introduit des risques d’erreur de syntaxe :
#define f(m) != #define f (m)
peut entrainer un «excès d’interprétation» (effets de bord)
#defineX(m) (m+m) i = 3;
printf ("%d\n",X(i++));/∗ X(i++) devient i++ + i++ ∗/
Attention aux parenthèses et aux accolades
#defineSKIP_SPACES(p, limit) { \ while (p < (limit )) { \
if (∗p++ != ’ ’) { \ p−−;break; }}}
if (s != NULL)
SKIP_SPACES (s, s+lim);
else { // produit erreur de compilation car ; avant else ! // ...
}
Il faut protéger la macro soit par do{...}while(0)
soit par (...).
Avantages des macros en C
plus rapide qu’un appel de fonction Indifférent aux types :
#definemin(X, Y) ((X) < (Y) ? (X) : (Y)) doublex,d;
int y,a;
char ∗p,c;
x = min(1.1, d); −→x = ((1.1) < (d) ? (1.1) : (d));
y = min(a, 2); −→y = ((a) < (2) ? (a) : (2));
c = min(a + 28, ∗p); −→z = ((a + 28) < (∗p) ? (a + 28) : (∗p));
Stringizing : op. #
Parfois on a envie de transformer un param d’une macro en chaine de car.
#defineWARN_IF(EXP) \
do{ if (EXP) \
fprintf ( stderr , "Warning: " #EXP "\n"); \ } while (0)
/∗ ou encore ∗/
#defineWARN_IF(EXP) ({ \
if (EXP) fprintf ( stderr , "Warning " #EXP "\n"); \ })
WARN_IF (x == 0);
−→
do{ if (x == 0) fprintf ( stderr , "Warning: " "x == 0" "\n"); }while(0);
fprintf(stderr, "Warning: " "x == 0" "\n") est identique à fprintf(stderr, "Warning: x == 0\n")
Autre exemple utile
Recherche du maximum d’un tableau d’un type quelconque
Btypeof est spécifique à gcc
#definemaxTab(t, taille ) ({ \ typeof(t [0]) max = t[0]; \ for (int i=1 ; i< taille ; i++) \
max = (max > t[i]) ? max : t[ i ]; \
max; \
})
#defineSIZE 10000 doubletabd[SIZE],d;
int tabn[SIZE],n;
/∗ ... ∗/
d = maxTab(tabd,SIZE);
n = maxTab(tabn,SIZE);
Concatenation par l’opérateur ##
Eviter les répétitions struct command {
char ∗name;
void (∗function ) (void);
};
struct command commands[ ] = { { "quit", quit_command }, { "help", help_command }, // ...
};
Les deux blocs peuvent être remplacés par :
#defineCOMMAND(NAME) { #NAME, NAME ## _command } struct command commands[ ] = {
COMMAND (quit), COMMAND (help), // ...
};
Macros avec un nbre variable d’arguments
Variadic Macros
Ici on profite aussi de deux macros prédéfinies :
#definemydebug(format,...) ({ \
if (debug) \
fprintf ( stderr , "[%s, ligne %d] " format "\n", \
__FUNCTION__, __LINE__, __VA_ARGS__); \ })
mydebug("resu=%d, total=%g\n", resu, total);
Le nom de la fonction et le numéro de ligne de l’appel seront affichés : [main, ligne 18] resu=12, total=16.22
B
Il faut dans ce cas toujours au moins 2 param.
X-macros
Exemple de code normal
typedef struct { int x,y,z, t ; double radius ; } starStruct ;
void print_star (const startStruct ∗ const star ) { printf ("%s= %d\n", "x", star−>x);
printf ("%s= %d\n", "y", star−>y);
/∗ ... puis z et t ... cela peut etre long ! ∗/
printf ("%s= %g\n", "radius", star−>radius);
}
Avec X-macros
#defineMALISTE \ X(x, int) \ X(y, int) \ // etc ...
X(radius, double) typedef struct {
#define X(member, type) type member;
MALISTE
#undef X } starStruct ;
#defineFORMAT_int "%d"
#defineFORMAT_double "%g"
void print_star (const starStruct ∗const star ) {
#define X(member, type) \
printf ("%s= " FORMAT_##type "\n", #member, star−>member);
MALISTE
#undef X }
Autre exemple
enumCOLOR { red, green, blue };
char ∗color_name[] = { [red] = "red", [green] = "green", [blue] = "blue"
};
int main() {
enumCOLOR c = red;
printf ("c=%s\n", color_name[c]);
return 0;
}
avec X-macros
Pourquoi les appeler X ?
#defineCOLOR_TABLE Y(red) Y(green) Y(blue)
#defineY(a) a, enumCOLOR {
COLOR_TABLE };
#undefX
#defineY(a) #a, char ∗color_name[ ] = {
COLOR_TABLE };
#undefX int main() {
enumCOLOR c = red;
printf ("c=%s\n", color_name[c]);
}