Pourquoi ces remplacements de macros consécutifs n’entraînent-ils pas une erreur?

Ce programme donne la sortie en tant que 5. Mais après avoir remplacé toutes les macros, il en résulte --5 . Cela devrait provoquer une erreur de compilation, en essayant de décrémenter le 5 . Mais il comstack et fonctionne bien.

 #include  #define A -B #define B -C #define C 5 int main() { printf("The value of A is %d\n", A); return 0; } 

Pourquoi n’y a-t-il pas d’erreur?

Voici les étapes pour la compilation de l’instruction printf("The value of A is %d\n", A); :

  • l’parsingur lexical produit les jetons de pré-traitement printf , ( , "The value of A is %dn" A , ) et ; .
  • A est une macro qui s’étend aux 2 jetons - et B
  • B est également une macro et est étendu à - et C
  • C est à nouveau une macro et est étendu à 5 .
  • les jetons sont ensuite convertis en jetons C, générant des erreurs de pré-traitement des jetons qui ne sont pas convertis en jetons C appropriés (ex: 0a ). Dans cet exemple, les jetons sont identiques.
  • le compilateur parsing la séquence obtenue conformément à la grammaire C: printf , ( , "The value of A is %d\n" - , - , 5 , ) ; correspond à un appel de fonction à printf avec 2 arguments: une chaîne de format et une expression constante - - 5 , évaluée à 5 lors de la compilation.
  • le code est donc équivalent à printf("The value of A is %d\n", 5); . Il produira la sortie:

     The value of A is 5 

Cette séquence de macros est développée en tant que jetons, et non pas ssortingctement en une séquence de caractères. Par conséquent, A ne se développe pas en tant que --5 , mais plutôt en tant que - -5 . Les bons compilateurs C insèrent un espace supplémentaire lors du prétraitement de la source en sortie textuelle afin de garantir que le texte résultant génère la même séquence de jetons lors de la réparation. Notez cependant que le standard C ne dit rien sur le prétraitement en sortie textuelle, il spécifie uniquement le prétraitement en tant que phase d’parsing et c’est un problème de qualité d’implémentation pour les compilateurs de ne pas introduire d’effets secondaires potentiels lors du prétraitement en sortie textuelle.

Il existe une fonctionnalité distincte permettant de combiner des jetons en nouveaux jetons dans le préprocesseur, appelée collage de jetons. Il nécessite un opérateur spécifique ## et est assez délicat à utiliser.

Notez également que les macros doivent être définies avec des parenthèses autour de chaque argument et des parenthèses autour du développement complet pour éviter les problèmes de priorité des opérateurs:

 #define A (-B) #define B (-C) #define C 5 

Deux tirets consécutifs ne sont pas combinés en un seul opérateur de pré-décrémentation -- car le préprocesseur C fonctionne avec des jetons individuels, ce qui permet d’insérer efficacement des espaces autour des substitutions de macros. Lancer ce programme avec gcc -E

 #define A -B #define B -C #define C 5 int main() { return A; } 

produit la sortie suivante:

 int main() { return - -5; } 

Notez l’espace après le premier - .

Selon la norme, les macros de remplacement sont effectuées au niveau des jetons de préprocesseur et non au niveau des caractères individuels (6.10.3.9):

Une directive de prétraitement de la forme

 # define identifier replacement-list new-line 

définit une macro de type object qui remplace chaque instance ultérieure du nom de la macro par la liste de remplacement des jetons de prétraitement constituant le rest de la directive.

Par conséquent, les deux tirets constituent deux jetons différents et sont donc séparés l’un de l’autre dans la sortie du pré-processeur.

Chaque fois que nous utilisons #include dans le programme C, le compilateur remplacera la variable par sa valeur, où qu’elle soit utilisée.

 #define A -B #define B -C #define C 5 

Ainsi, lorsque nous imprimons A, il s’exécutera aux étapes suivantes.

A => – B

B => – C

A => – (- C) => C

Ainsi, lorsque nous imprimons la valeur de A, elle s’élève à 5.

Généralement, ces instructions #define sont utilisées pour déclarer la valeur des constantes à utiliser dans le code.

Pour plus d’informations, voir ce lien sur la directive #define