54 votes

Le préprocesseur C supprime-t-il les commentaires ou développe-t-il les macros en premier ?

Considérez cette (horrible, terrible, pas bonne, très mauvaise) structure de code :

#define foo(x) // commented out debugging code

// Misformatted to not obscure the point
if (a)
foo(a);
bar(a);

J'ai vu les préprocesseurs de deux compilateurs générer des résultats différents sur ce code :

if (a)
bar(a);

// and

if (a)
;
bar(a);

Évidemment, c'est une mauvaise chose pour une base de code portable.

Ma question : Qu'est ce que le préprocesseur est censé faire avec ça ? Elider les commentaires d'abord, ou développer les macros d'abord ?

32voto

Reed Copsey Points 315315

Malheureusement, l'original Spécification C de l'ANSI exclut spécifiquement toute fonctionnalité du préprocesseur dans la section 4 ("Cette spécification ne décrit que le langage C. Elle ne prévoit aucune disposition pour la bibliothèque ou le préprocesseur"). Elle ne contient aucune disposition concernant la bibliothèque ou le préprocesseur").

Le site Spécification C99 gère cela explicitement, cependant. Les commentaires sont remplacés par un espace unique lors de la "phase de traduction", qui a lieu avant l'analyse des directives de prétraitement. (Section 6.10 pour plus de détails).

VC++ et le Compilateur GNU C Les deux suivent ce paradigme - les autres compilateurs peuvent ne pas être conformes s'ils sont plus anciens, mais s'ils sont conformes à C99, vous devriez être en sécurité.

10voto

Michael Burr Points 181287

Comme décrit dans ce copié-collé de description des phases de traduction de la norme C99, la suppression des commentaires (ils sont remplacés par un simple espace) intervient dans la phase de traduction 3, tandis que les directives de prétraitement sont traitées et les macros sont développées dans la phase 4.

Dans la norme C90 (que je n'ai qu'en version papier, donc pas de copier-coller) ces deux phases se déroulent dans le même ordre, bien que la description des phases de traduction soit légèrement différente dans certains détails de la norme C99 - le fait que les commentaires soient supprimés et remplacés par un seul caractère d'espacement avant que les directives de prétraitement soient traitées et les macros développées n'est pas différent.

Là encore, la norme C++ prévoit que ces deux phases se déroulent dans le même ordre.

Pour ce qui est de la façon dont le // Les commentaires ' doivent être traités, la norme C99 le dit (6.4.9/2) :

Sauf à l'intérieur d'une constante de caractère, d'un littéral de chaîne de caractères ou d'un commentaire, les caractères // introduisent un commentaire qui inclut tous les caractères multi-octets jusqu'au, mais pas le, prochain caractère de nouvelle ligne. le prochain caractère de nouvelle ligne.

Et la norme C++ dit (2.7) :

Les caractères // commencent un commentaire, qui se termine par le prochain caractère de nouvelle ligne caractère.

Votre premier exemple est donc clairement une erreur de la part de ce traducteur - le ' ; après le caractère foo(a) doit être conservé lorsque le foo() est développée - les caractères de commentaire ne doivent pas faire partie du "contenu" de la macro the foo() macro.

Mais comme vous êtes confronté à un traducteur bogué, vous pouvez modifier la définition de la macro en :

#define foo(x) /* junk */

pour contourner le bogue.

Cependant (et je m'éloigne du sujet ici...), étant donné que la coupure de ligne (les barres obliques inversées juste avant une nouvelle ligne) se produit avant que les commentaires ne soient traités, vous pouvez rencontrer quelque chose comme ce bout de code méchant :

#define evil( x) printf( "hello "); // hi there, \
                 printf( "%s\n", x); // you!

int main( int argc, char** argv)
{
    evil( "bastard");

    return 0;
}

Ce qui pourrait surprendre celui qui l'a écrit.

Ou encore mieux, essayez ce qui suit, écrit par quelqu'un (certainement pas moi !) qui aime les commentaires en forme de boîte :

int main( int argc, char** argv)
{
                            //----------------/
    printf( "hello ");      // Hey, what the??/
    printf( "%s\n", "you"); // heck??         /
                            //----------------/
    return 0;
}

Selon que votre compilateur traitera par défaut trigraphes ou non (les compilateurs sont censés le faire, mais comme les trigraphes surprennent presque tous ceux qui les rencontrent, certains compilateurs décident de les désactiver par défaut), vous pouvez obtenir ou non le comportement que vous souhaitez - quel que soit ce comportement, bien sûr.

5voto

Jim Lewis Points 18753

Selon MSDN les commentaires sont remplacés par un espace unique dans la phase de tokénisation, qui a lieu avant la phase de prétraitement où les macros sont développées.

4voto

Vitali Points 1112

Ne mettez jamais de commentaires // dans vos macros. Si vous devez mettre des commentaires, utilisez /* */. De plus, vous avez une erreur dans votre macro :

#define foo(x) do { } while(0) /* junk */

De cette façon, foo peut être utilisé en toute sécurité. Par exemple :

if (some condition)
    foo(x);

ne lèvera jamais une erreur de compilation, que foo soit défini ou non par une expression.

1voto

Joshua Points 13231

Je crois me souvenir que la conformité nécessite trois étapes :

  1. strip
  2. développer les macros
  3. encore une fois

La raison en est que le compilateur est capable d'accepter directement les fichiers ".i".

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