tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

[C] TP : les macros

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 :


$ DEBUG=1 make 

$ 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.