63 votes

Comment le préprocesseur C gérer les dépendances circulaires?

Je veux savoir comment le préprocesseur C gère les dépendances circulaires (de #définit). C'est mon programme

#define ONE TWO 
#define TWO THREE
#define THREE ONE

int main()
{
    int ONE, TWO, THREE;
    ONE = 1;
    TWO = 2;
    THREE = 3;
    printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE,  TWO, THREE);
}

Voici la sortie du préprocesseur. Je ne suis pas en mesure de comprendre pourquoi la sortie est en tant que tel. Je voudrais savoir les différentes étapes d'un préprocesseur, qui prend dans ce cas pour donner le résultat suivant.

# 1 "check_macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "check_macro.c"

int main()
{
 int ONE, TWO, THREE;
 ONE = 1;
 TWO = 2;
 THREE = 3;
 printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE, TWO, THREE);
}

Je suis l'exécution de ce programme sur linux 3.2.0-49-generic-pae et de la compilation de la version de gcc 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5).

76voto

rici Points 45980

Alors que d'un pré-processeur de macro est en cours d'expansion, cette macro est le nom n'est pas élargi. Donc, tous les trois de vos symboles sont définis comme eux-mêmes:

ONE -> TWO -> THREE -> ONE (not expanded because expansion of ONE is in progress)
TWO -> THREE -> ONE -> TWO (        "                         TWO      "        )
THREE -> ONE -> TWO -> THREE (      "                         THREE    "        )

Ce comportement est défini par le §6.10.3.4 de la norme (le numéro de la section de la C11 projet, bien que, autant que je sache, le libellé et la numérotation de l'article est inchangé depuis C89). Quand un nom de macro est rencontré, il est remplacé par sa définition (et # et ## préprocesseur opérateurs sont traitées, ainsi que les paramètres de la fonction-comme les macros). Le résultat est à nouveau analysés pour plus de macros (dans le contexte du reste du fichier):

2/ Si le nom de la macro de remplacement est trouvé au cours de cette analyse de la liste de remplacement (pas y compris le reste du fichier source du prétraitement des jetons), il n'est pas remplacé. En outre, si toutes les remplacements de rencontrer le nom de la macro d'être remplacé, il n'est pas remplacé...

La clause revient à dire que n'importe quel jeton qui n'est pas remplacé en raison d'un appel récursif est effectivement "surgelés": il ne sera jamais remplacé:

... Ces nonreplaced nom de la macro de prétraitement des jetons ne sont plus disponible pour d'autres de remplacement, même si ils sont plus tard (ré)examinée dans des contextes dans lequel le nom de la macro de prétraitement jeton aurait autrement été remplacé.

La situation à la dernière phrase se réfère rarement dans la pratique, mais ici, c'est le cas le plus simple je pense:

#define two one,two
#define a(x) b(x)
#define b(x,y) x,y
a(two)

Le résultat est one, two. two est élargi à d' one,two pendant le remplacement d' a, ainsi que l'élargissement two est marqué comme complètement étendu. Par la suite, b(one,two) est élargi. Ce n'est plus dans le contexte de la substitution d' two, mais l' two qui est le deuxième argument de b a été gelé, il n'est pas développé à nouveau.

17voto

Eric Lippert Points 300275

Votre question est résolue par la publication de l'ISO/IEC 9899:TC2 section 6.10.3.4 "Rescanner et davantage de remplacement", au paragraphe 2, que je cite ici pour plus de commodité, dans l'avenir, veuillez considérer la lecture de la specificaftion quand vous avez une question à propos de la spécification.

Si le nom de la macro de remplacement est trouvé au cours de cette analyse de la liste de remplacement (pas y compris le reste du fichier source du prétraitement des jetons), il n'est pas remplacé. En outre, si toutes les remplacements de rencontrer le nom de la macro d'être remplacé, il n'est pas remplacé. Ces nonreplaced nom de la macro de prétraitement des jetons ne sont plus disponible pour de plus amples remplacement, même si ils sont plus tard (ré)examinée dans les contextes dans lesquels que le nom de la macro de prétraitement jeton aurait autrement été remplacé.

9voto

R Sahu Points 24027

https://gcc.gnu.org/onlinedocs/cpp/Self-Referential-Macros.html#Self-Referential-Macros les réponses à la question sur l'auto-référentielle des macros.

L'essentiel de la réponse est que lorsque le pré-processeur trouve autoréférentiel des macros, il ne s'étend pas à tous.

Je soupçonne que la même logique est utilisé pour prévenir l'expansion de la circulaire macros définies. Sinon, le préprocesseur va être dans une infinie expansion.

4voto

Matt McNabb Points 14273

Dans votre exemple, vous effectuez le traitement de la macro avant de définir des variables de même nom, donc indépendamment de ce que le résultat le traitement de la macro est, vous pouvez toujours imprimer 1, 2, 3!

Voici un exemple où les variables sont définies en premier:

#include <stdio.h>
int main()
{
    int A = 1, B = 2, C = 3;
#define A B
#define B C
//#define C A
    printf("%d\n", A);
    printf("%d\n", B);
    printf("%d\n", C);
}

Ce imprime 3 3 3. Un peu insidieusement, décommentant #define C A change le comportement de la ligne, printf("%d\n", B);

4voto

Ilmari Karonen Points 20585

Voici une belle démonstration du comportement décrit dans le rici du et Eric Lippert de réponses, c'est à dire que le nom de la macro n'est pas re-développée si elle est à nouveau rencontrés alors qu'il est déjà l'expansion de la même macro.

Le contenu de test.c:

#define ONE 1, TWO
#define TWO 2, THREE
#define THREE 3, ONE

int foo[] = {
  ONE,
  TWO,
  THREE
};

Sortie d' gcc -E test.c (à l'exclusion initiale # 1 ... lignes):

int foo[] = {
  1, 2, 3, ONE,
  2, 3, 1, TWO,
  3, 1, 2, THREE
};

(Je poste comme un commentaire, mais substantielle, y compris les blocs de code dans les commentaires est un peu maladroite, je suis donc ce qui en fait un Wiki de la Communauté de répondre à la place. Si vous pensez qu'il serait mieux compris dans le cadre d'une réponse, n'hésitez pas à la copier et à me demander de supprimer ce CW version.)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X