2010-11-12 42 views
13

Voici un exemple pertinent. Ce n'est évidemment pas valide C, mais j'ai juste affaire au préprocesseur ici, donc le code n'a pas réellement à compiler.Lors de l'expansion de macro C, existe-t-il un cas spécial pour les macros qui s'étendent à "/ *"?

#define IDENTITY(x) x 
#define PREPEND_ASTERISK(x) *x 
#define PREPEND_SLASH(x) /x 

IDENTITY(literal) 
PREPEND_ASTERISK(literal) 
PREPEND_SLASH(literal) 
IDENTITY(*pointer) 
PREPEND_ASTERISK(*pointer) 
PREPEND_SLASH(*pointer) 

course à pied préprocesseur de gcc sur elle:

gcc -std=c99 -E macrotest.c 

Cela donne:

(...) 

literal 
*literal 
/literal 
*pointer 
**pointer 
/*pointer 

S'il vous plaît noter l'espace supplémentaire dans la dernière ligne. Cela ressemble à une fonctionnalité pour empêcher les macros de s'étendre vers "/ *", ce qui, je suis sûr, est bien intentionné. Mais en un coup d'œil, je n'ai rien trouvé de ce comportement dans la norme C99. Là encore, je suis inexpérimenté au C. Quelqu'un peut-il nous éclairer à ce sujet? Où est-ce spécifié? J'imagine qu'un compilateur adhérant à C99 ne devrait pas simplement insérer des espaces supplémentaires pendant l'expansion de macro juste parce qu'il empêcherait probablement des erreurs de programmation.

Répondre

15

Le code source est déjà segmenté avant d'être traité par CPP.

Donc ce que vous avez est un / et un jeton * qui ne sera pas combiné implicitement à un /* « jeton » (puisque/* n'est pas vraiment un jeton de préprocesseur je l'ai mis dans « »).

Si vous utilisez -E pour sortir une source prétraite, le CPP doit insérer un espace afin d'éviter que /* soit lu par une passe de compilateur ultérieure.

La même caractéristique empêche de deux par ex. + des signes de différentes macros combinées en un jeton ++ en sortie.

La seule façon de se coller vraiment deux jetons préprocesseur en même temps que l'opérateur ##:

#define P(x,y) x##y 

... 

P(foo,bar) 

résultats dans le jeton foobar

P(+,+) 

résultats dans le ++ jeton, mais

P(/,*)  

n'est pas valide depuis /* n'est pas un jeton de préprocesseur valide.

+0

+1. Je pense que l'idée clé est que la sortie de -E n'est en effet pas spécifiée par la norme.La norme parle du programme constitué d'une séquence de jetons de prétraitement, puis plus tard, il est converti en une séquence de jetons. Il revient entièrement au préprocesseur de représenter ces séquences, et dans ce cas comment les sérialiser en un fichier sous la forme d'une séquence de * octets *. Bien sûr, la seule sérialisation viable est celle qui sera lue comme une série équivalente de jetons de prétraitement, de sorte que, comme vous le dites, elle doit mettre des espaces entre deux jetons qui ensemble formeraient un. –

+0

Je suis d'accord à 100%, je voulais écrire quelque chose comme votre explication, mais je n'ai pas eu le temps. –

+4

Bonne réponse, même si j'ai deux commentaires nitpicky: Il n'y a pas de jeton '/ *'; les commentaires sont supprimés de la source avant d'être marqués. Vous pouvez former un jeton «++» à partir de deux jetons «+» en utilisant «##». –

5

Le comportement du pré-processeur est standardisé. Dans le résumé au http://en.wikipedia.org/wiki/C_preprocessor, les résultats que vous observez sont l'effet de:

"3: Tokenization - Le préprocesseur divise le résultat en jetons de prétraitement et espaces blancs, il remplace les commentaires par des espaces".

Cela a lieu avant:

"4: Expansion macro et directive manipulation".