quelques cas légitime, selon moi:
-- en C, contrairement a C++, il n'est pas possible d'utiliser un const pour déclarer un tableau global
const uint size = 8;
int tab[size]; // invalide
-- passer des arguments implicitement
#define printf(format_string, ...) fprintf(STDIN, format_string, ...)
-- le standard C dit que toutes chaines de caractères cote a cote sont considérées comme une seule
printf("x""y""z") est la même chose que printf("xyz")
ca permet des trucs sympas comme
#define LOG_ERROR(x, ...) printf("ERROR"__LINE__":"x"\n", ...)
-- manipulation de bits. eg: des pages mémoire où la taille est un multiple de 8: les 3 bits inférieur sont toujours a zero et peuvent être utilises pour un tag
struct mem_page {
uint size_tag;
void *address;
}
#define PAGE_SIZE(x) (x & -8)
#define PAGE_TAG(x) (x & 7)
-- plus délicat, faire de l'abstraction sur les structures de données. par exemple un système d'objets ou chaque objet partage des champs communs (un header). ici, utiliser un macro au lieu d'une fonction inline permet d'avoir une l-value.
struct __header { // cuisine interne, sujet a modification
uint size;
uint type_id;
}
#define OBJ_HEADER struct __header
#define OBJ_SIZE(x) (x->__header.size)
struct obj_int {
OBJ_HEADER;
...
}
OBJ_SIZE(foo) = 128; // invalide si OBJ_SIZE est une fonction