résultat étrange sur l’expansion macro

Considérez l’extrait de code suivant

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

Sortie

 The value of A is 5 

Mais cela ne devrait pas du tout être compilé car après l’extension, il devrait ressembler à printf("The value of A is %d\n", --5); et alors il devrait donner une erreur de compilation en disant lvalue requirejs. N’est-ce pas?

Passez-le à l’option -E (Ex: gcc -E ac ). Cela produira un code source prétraité.

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

Donc, il introduira un espace entre -5 et -5 et ne sera donc pas considéré comme un opérateur de décrémentation -- , donc printf affichera 5.

La documentation GCC sur l’ espacement des jetons fournit des informations sur les raisons pour lesquelles un espace supplémentaire est créé:

En premier lieu, considérons un problème qui ne concerne que le pré-processeur autonome: il doit être garanti que la relecture de la sortie prétraitée produira un stream de jetons identique. Sans prendre de mesures spéciales, cela pourrait ne pas être le cas en raison de la substitution de macros. Par exemple:

  #define PLUS + #define EMPTY #define f(x) =x= +PLUS -EMPTY- PLUS+ f(=) ==> + + - - + + = = = not ==> ++ -- ++ === 

Une solution serait simplement d’insérer un espace entre tous les jetons adjacents. Cependant, nous aimerions limiter l’insertion d’espace au minimum, à la fois pour des raisons esthétiques et parce que cela pose des problèmes aux personnes qui essaient encore d’abuser du pré-processeur pour des éléments tels que les sources Fortran et Makefiles.

Pour l’instant, il suffit de noter que lorsque des jetons sont ajoutés (ou supprimés, comme le montre l’exemple EMPTY) du stream de jetons lexed d’origine, nous devons vérifier le collage accidentel de jetons. Nous appelons cela éviter de la pâte. L’ajout et la suppression de jetons ne peuvent se produire qu’en raison de l’expansion des macros, mais un collage accidentel peut se produire à plusieurs endroits: avant et après chaque remplacement de macro, chaque remplacement d’argument, ainsi que chaque jeton créé par les opérateurs # et ## .

Je ne pense pas. Même l’expansion de macros étant un traitement de texte, il est impossible de créer un jeton à partir de limites de macros. Par conséquent, il s’agit de -(-5) , et non de --5 , car -- est un simple jeton.

Le préprocesseur introduit un espace entre l’expansion de B et C :

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

avec sortie (généré via cpp < test.c )

 # 1 "test.c" # 1 "" 1 # 1 "" 3 # 329 "" 3 # 1 "" 1 # 1 "" 2 # 1 "test.c" 2 - -5 

En langage C, le code source du programme est divisé en jetons de prétraitement à un stade très précoce de la traduction (phase 3), avant la substitution de macros (phase 4). Plus tard (à la phase 7), les jetons de prétraitement sont convertis en jetons réguliers qui sont ensuite introduits dans l’parsingur syntaxique et sémantique du compilateur proprement dit (voir “5.1.1.2 Phases de traduction” dans la spécification du langage).

La phase 3 est l’étape où les jetons de prétraitement des futurs opérateurs du langage C et d’autres éléments lexicaux sont formés (identifiants, nombres, ponctuateurs, littéraux de chaîne, etc.). Des ponctuateurs multi-caractères tels que -- , >>= , etc., sont alors formés. stade précoce. Pour obtenir éventuellement un jeton pour -- opérateur à la phase 7, vous devez disposer de -- tôt comme pointeur complet à la phase 3. Aucune concaténation de pointeur supplémentaire ne se produit lors du passage des jetons de prétraitement aux jetons normaux à la phase 7, ce qui signifie que deux témoins adjacents - détecteurs détectés à la phase 3 ne deviendront PAS un seul jeton -- à la phase 7. Le compilateur proprement dit n’aura jamais la chance de voir ces deux signaux adjacents - et un seul jeton -- .

En d’autres termes, en C, vous ne pouvez pas utiliser le préprocesseur pour concaténer des éléments en les plaçant l’un à côté de l’autre. C’est pourquoi le préprocesseur dispose de fonctionnalités dédiées telles que ## pour faciliter la concaténation. Et ## est ce que vous devez utiliser pour concaténer deux jetons en un seul.

En passant, il n’est pas correct d’expliquer ce comportement en affirmant que le préprocesseur placera un caractère espace entre vos - caractères. Rien de tel n’est présent dans la spécification de langue. Ce qui se passe réellement, c’est que, dans les structures internes du compilateur, vos jetons restnt pour toujours sous la forme de deux jetons distincts. Comment le préprocesseur et le compilateur y parviennent est le détail de leur implémentation interne. Dans les implémentations avec préprocesseur et compilateur proprement couplés (par exemple, des modules complètement indépendants qui communiquent via une représentation textuelle intermédiaire), l’injection d’un espace entre des ponctuateurs adjacents est en soi un moyen naturel d’implémenter la séparation requirejse des jetons.