Date de première publication : 2026/10/19
L'idée de ce TP est de vous faire découvrir les macros commandes.
Ne faites que les parties dont les thèmes ont été abordés en cours !
Que fait le préprocesseur ?
Le préprocesseur effectue des opérations de texte avant la compilation à proprement parler.
Pour voir le résultat, il suffit de compiler avec l'option -E :
#include <stdio.h>
#define T 10
int tab[T];
int main() {
int i;
for(i=0; i<T; ++i)
tab[i] = 0;
return 0;
}
Mise en oeuvre des macros vues en cours
Coder dans un fichier la macro CARRE:
#define CARRE1(A) A*A
Tester avec les exécutions suivantes :
printf("%d", CARRE(i));
printf("%f", CARRE(5.1));
printf("%d", CARRE(i+1));
printf("%d", CARRE(i++));
printf("%s", CARRE("loic"));
Vous pouvez également observer les effets de bord avec la macro MAX
Aide au débogage
Mettre en place la macro DUMP pour afficher la valeur d'une variable :
#define DUMP(X) \
printf ("[TRACE] " #X " == "); \
_Generic((X), \
int : printf("%d (int)", X), \
float : printf("%f (float)", X), \
void* : printf("%p (void*)", X) , \
default : printf("inconnu" ))
Vous pouvez bien entendu enrichir les types disponibles (par exemple, afficher une structure si vous savez ce que c'est) et tester que cela marche bien :
int a = 1;
int * p = &a;
DUMP(a);
DUMP((void*)p);
Adapter maintenant la définition de la macro DUMP en fonction de la
constante symbolique DEBUG. Pour rappel, vous pouvez :
- activer/désactiver la constance de manière explicite avec
#define - en spécifiant la valeur à la compilation lors de l'appel à
make
$ DEBUG=1 make
- ou par une option
$ make -DDEBUG
Génération de code
La duplication de code n'est pas quelque chose que l'on veut voir dans nos programmes, sauf si c'est le compilateur qui travaille... Une macro classique de max peut être définie de la manière suivante :
#define MAX_DU_COURS(A,B) (((A)>(B))?(A):(B))
On a vu les effets de bord en cours ...
Écrivons une macro qui génère les fonctions "maximum" dépendantes des types: max_int,
max_double, ...
#define GENERATE_MAX_FUNCTION(TYPE) \
TYPE max_##TYPE(TYPE a, TYPE b) { \
return (a > b) ? a : b; \
}
Il suffit d'écrire les fonctions avec des appels à la macro GENERATE :
GENERATE_MAX_FUNCTION(int) // pas de ;
GENERATE_MAX_FUNCTION(long)
GENERATE_MAX_FUNCTION(float)
GENERATE_MAX_FUNCTION(double)
On peut vérifier que le préprocesseur (gcc -E)a bien écrit quatre fonctions max, c'est joli, non ?
Pour des questions de performance, les fonctions peuvent être static inline. Dans cette situation, c'est
le compilateur qui décide si une fonction est générée ou pas.
Testons tout cela :
int main() {
int i= 3, j=7;
double a = 2.0, b = 6.0;
printf("%d ", max_int(++i, ++j));
printf("%d %d\n", i, j);
printf("%f ", max_double(++a, ++b));
printf("%f %f\n", a, b);
return 0;
}
La dernière pirouette de code est d'utiliser _Generic pour n'avoir qu'une seule "fonction" :
#define MAX(a, b) _Generic((a), \
int: max_int, \
float: max_float, \
double: max_double \
)(a, b)
avec le test qui va bien :
int main() {
int i= 3, j=7;
double a = 2.0, b = 6.0;
printf("%d ", MAX(++i, ++j));
printf("%d %d\n", i, j);
printf("%f ", MAX(++a, ++b));
printf("%f %f\n", a, b);
return 0;
}
C'est chouette, non ?
Malheureusement, on ne peut pas appeler une macro comme MAX_DU_COURS dans la clause default de _Generic
mais on peut afficher un message sur la sortie d'erreur standard pour dire qu'il n'y a pas de fonction max correspondante.


