Maison > développement back-end > C++ > le corps du texte

#define INC(a) INC(a ?

Susan Sarandon
Libérer: 2024-10-22 06:10:31
original
477 Les gens l'ont consulté

#define INC(a) INC(a ?

Cette macro fait-elle planter GCC ? Lisez et vous aurez la réponse

Le but de cet article est de vous faire découvrir le magnifique univers des macros en C.

Une directive préprocesseur

En C, les lignes qui commencent par un # sont interprétées par le compilateur lors de la compilation des fichiers sources. On les appelle des directives du préprocesseur. Les macros en font partie.

Petit point historique :

Les macros en langage C ont été introduites avec la première norme du langage C, appelée ANSI C (ou C89),
qui a été standardisée par l'American National Standards Institute (ANSI) en 1989.

Cependant, avant cette standardisation, les macros faisaient déjà partie du langage C classique (ou K&R C) utilisé dans les années 1970.
Le compilateur C original, développé par Dennis Ritchie pour le système d'exploitation UNIX, incluait déjà une forme rudimentaire de macros via le préprocesseur, permettant des définitions avec #define.

Define

#define SENS_DE_LA_VIE 3.14

/* ... */

printf("%f\n", SENS_DE_LA_VIE);
Copier après la connexion
Copier après la connexion
Copier après la connexion

Le define a un fonctionnement assez simple à comprendre : le compilateur remplace toutes les occurrences dans le code par la valeur définie. Il fonctionne avec la syntaxe suivante #define . On a pour convention de mettre le nom en majuscule, la valeur quant à elle est optionnelle.

Un peu comme un "Ctrl-f et remplacer".

Mama, la macro

On peut utiliser les define pour définir des fonctions que l'on pourra utiliser dans notre code.

#define INC(a) a++ 
#define MULTI_LINE(a,b) a = b; \
                        b = 0; 


INC(my_variable); 
MULTI_LINE(my_variable, foobar) 
// Je souligne le fait qu'il peut ne pas y avoir de ';' en fin de ligne 

// Cela donnera  
my_variable++;
my_variable = foobar;
foobar = 0;
Copier après la connexion
Copier après la connexion
Copier après la connexion

If or not if

Nous pouvons déclarer des macros de manière conditionnelle.
Si un nom est déjà défini alors on exécute le bout de code suivant.

#ifdef DEBUG
// Je souligne qu'il est rarement conseillé d'utiliser des printf() en debug
// et que nous avons brisé la règle du nom des macros en MAJ.
#define return printf("(%s:%d)\n", __FUNCTION__, __LINE__);  return
#endif /* ! DEBUG */

int main(void) {
    return 1;
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Dans ce cas, j'utilise un #ifndef, mais il existe aussi :

  • #ifdef
  • #if
  • #else
  • #elif
#if (X == 1)
#define Y 2
#elif (X == 2)
#define Y "Ami de la bonne blague, bonjour !"
#else
#define Y NULL
#endif /* ! X */

/* ... */

int main(void) {
    #if (X == 1)
    printf("%d\n", Y);
    #elif (X == 2)
    printf("%s\n", Y);
    #else
    printf("%p\n", Y);
    #endif /* ! X */
}
Copier après la connexion
Copier après la connexion

On aime bien signaler avec un commentaire en bloc la fin des #if. C'est une convention qui permet de mieux se repérer dans le code.

Macros prédéfinies

Vous avez pu voir dans l'exemple précédent que j'utilisais les mots-clés __FUNCTION__ et __LINE__.
Comme vous pouvez vous en douter, ce sont des macros que le compilateur va remplacer par la bonne valeur.

Il existe une liste de macros prédéfinies Common Predifined.

À noter qu'il existe des macros dites System specific.

Petite liste non exhaustive :

  • __DATE__ : Jan 14 2012
  • __GNUC__ : Version majeure de GCC
  • __TIME__ : 15:12:18
  • __INCLUDE_LEVEL__ : La profondeur des includes en commençant par 0
  • __BASE_FILE__ : Le nom du fichier actuel

Vers l'infini et au-delà des arguments

#define SENS_DE_LA_VIE 3.14

/* ... */

printf("%f\n", SENS_DE_LA_VIE);
Copier après la connexion
Copier après la connexion
Copier après la connexion

Ici, on peut voir que l'on génère des macros variadiques, surtout utiles lors de la création de logs.
(Même si ce n'est pas une bonne idée de faire des logs avec des printf.)

X-Macro

Pour cela, nous allons devoir créer un fichier externe, souvent nommé en *.def bien qu'il n'existe pas de convention.

#define INC(a) a++ 
#define MULTI_LINE(a,b) a = b; \
                        b = 0; 


INC(my_variable); 
MULTI_LINE(my_variable, foobar) 
// Je souligne le fait qu'il peut ne pas y avoir de ';' en fin de ligne 

// Cela donnera  
my_variable++;
my_variable = foobar;
foobar = 0;
Copier après la connexion
Copier après la connexion
Copier après la connexion
#ifdef DEBUG
// Je souligne qu'il est rarement conseillé d'utiliser des printf() en debug
// et que nous avons brisé la règle du nom des macros en MAJ.
#define return printf("(%s:%d)\n", __FUNCTION__, __LINE__);  return
#endif /* ! DEBUG */

int main(void) {
    return 1;
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Ce genre de macro est extrêmement utile. Je dois reconnaître qu'on la retrouve rarement dans un code source, mais elle permet de modifier le fonctionnement du programme sans pour autant devoir modifier le code source. Fun fact, elle est souvent utilisée dans la création de kernels. Elle permet de générer les structures globales comme l'IDT et la GDT.

Les problèmes

Attention : Petite mise au point d'abord, les macros sont des outils formidables mais il faut faire attention. Vous ne devez surtout pas utiliser ce genre de macro :

#if (X == 1)
#define Y 2
#elif (X == 2)
#define Y "Ami de la bonne blague, bonjour !"
#else
#define Y NULL
#endif /* ! X */

/* ... */

int main(void) {
    #if (X == 1)
    printf("%d\n", Y);
    #elif (X == 2)
    printf("%s\n", Y);
    #else
    printf("%p\n", Y);
    #endif /* ! X */
}
Copier après la connexion
Copier après la connexion

Prenons un exemple : MIN(2 5, fibo(25))

Problème n°1

MIN(2 5, fibo(25)) => (2 5 < fibo(25) ? 2 5 : fibo(25))

Ici le problème est la priorité de calcul. Le compilateur va d'abord effectuer la comparaison puis l'addition, donc 2 (1). On corrige cela par l'ajout de parenthèses en utilisant les arguments des macros.

// Ici, l'opérateur ## est l'opérateur de concaténation
#define DEBUG_PRNTF(fmt, ...) printf("LOG" ## fmt, __VA_ARGS__);
Copier après la connexion

Comme vous ne savez jamais ce que vos utilisateurs vont passer en paramètre, mettez toujours des parenthèses sur les arguments.

Problème n°2

MIN(2 5, fibo(25)) => (2 5 < fibo(25) ? 2 5 : fibo(25))

On remarque que le compilateur fait un remplacement bête et méchant, ce qui veut dire que l'on va calculer deux fois fibo(25). Je vous laisse imaginer si c'est une implémentation récursive.

Pour fixer ce problème, nous déclarons une variable intermédiaire avant le if.

Macros utiles

// color.def
X(NC, "\e[0m", "No Color", 0x000000) 
X(BLACK, "\e[0;30m", "Black", 0x000000) 
X(GRAY, "\e[1;30m", "Gray", 0x808080) 
X(RED, "\e[0;31m", "Red", 0xFF0000) 
X(LIGHT_RED, "\e[1;31m", "Light Red", 0xFF8080) 
X(GREEN, "\e[0;32m", "Green", 0x00FF00) 
X(LIGHT_GREEN, "\e[1;32m", "Light Green", 0x80FF80) 
X(BROWN, "\e[0;33m", "Brown", 0xA52A2A) 
X(YELLOW, "\e[1;33m", "Yellow", 0xFFFF00) 
X(BLUE, "\e[0;34m", "Blue", 0x0000FF) 
X(LIGHT_BLUE, "\e[1;34m", "Light Blue", 0xADD8E6) 
X(PURPLE, "\e[0;35m", "Purple", 0x800080) 
X(LIGHT_PURPLE, "\e[1;35m", "Light Purple", 0xEE82EE) 
X(CYAN, "\e[0;36m", "Cyan", 0x00FFFF) 
X(LIGHT_CYAN, "\e[1;36m", "Light Cyan", 0xE0FFFF) 
X(LIGHT_GRAY, "\e[0;37m", "Light Gray", 0xD3D3D3) 
X(WHITE, "\e[1;37m", "White", 0xFFFFFF)
Copier après la connexion

Là, on s'amuse

Ici, c'est du code purement overkill juste pour le fun. Je ne vous conseille pas forcément d'utiliser ces macros dans votre code.
Je me fais juste plaisir (faut bien dans la vie).

Un auto free

typedef struct {
    const char *name;        
    const char *ansi_code;  
    const char *description;
    unsigned int rgb;      
} Color;

#define X(NAME, ANSI, DESC, RGB) { #NAME, ANSI, DESC, RGB },
Color colors[] = {
    #include "color.def"
};
#undef X

#define X(NAME, ANSI, DESC, RGB) printf("%s (%s) = %s\n", #NAME, DESC, #RGB);
void print_colors() {
    // Bien entendu, on pourrait itérer sur la structure créée mais c'est une illustration
    #include "color.def"
}
#undef X
Copier après la connexion

Je vous laisse tester avec un petit -fsanitize=address. C'est vraiment une dinguerie. On pourrait même voir une amélioration de la fonction auto_free qui prend en paramètre une chaîne de caractères du nom de notre structure pour faire un switch.

Get time

Fonction plus chill où l'on calcule juste le temps d'exécution de notre fonction. Très utile pour faire du benchmark.

#define SENS_DE_LA_VIE 3.14

/* ... */

printf("%f\n", SENS_DE_LA_VIE);
Copier après la connexion
Copier après la connexion
Copier après la connexion

Define Error

Petite X-macro qui prend une macro en argument et qui l'expand.

#define INC(a) a++ 
#define MULTI_LINE(a,b) a = b; \
                        b = 0; 


INC(my_variable); 
MULTI_LINE(my_variable, foobar) 
// Je souligne le fait qu'il peut ne pas y avoir de ';' en fin de ligne 

// Cela donnera  
my_variable++;
my_variable = foobar;
foobar = 0;
Copier après la connexion
Copier après la connexion
Copier après la connexion

Génération de tests automatisés

Ici, on génère carrément des fonctions entières avec une macro, parce que le C n'a aucune limite. Moi aussi ?

#ifdef DEBUG
// Je souligne qu'il est rarement conseillé d'utiliser des printf() en debug
// et que nous avons brisé la règle du nom des macros en MAJ.
#define return printf("(%s:%d)\n", __FUNCTION__, __LINE__);  return
#endif /* ! DEBUG */

int main(void) {
    return 1;
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

RTFM

Il est maintenant l'heure de conclure. Nous avons vu plein de choses très cool. Et si jamais vous êtes tentés, libre à vous de découvrir les macros par vous-même. Il reste encore plein de choses à voir.
Donc, conclusion : RTFM.

PS : Pour ce qui est du titre, les macros ne sont pas récursives, elles ne s'expandent qu'avec une profondeur de 1 et dans notre cas présent, GCC va faire une implicit_declaration sur INC et crash.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!