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

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

#define foo(x) // commented out debugging code // Misformatted to not obscure the point if (a) foo(a); bar(a); 

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

 if (a) bar(a); 

et

 if (a) ; bar(a); 

Evidemment, c’est une mauvaise chose pour une base de code portable.

Ma question: Qu’est-ce que le préprocesseur est supposé faire avec cela? Elider les commentaires en premier ou développer les macros en premier?

Malheureusement, la spécification ANSI C d’ origine exclut spécifiquement toute fonctionnalité de préprocesseur de la section 4 (“Cette spécification ne décrit que le langage C. Il ne prévoit aucune disposition pour la bibliothèque ou le préprocesseur”).

La spécification C99 gère cette explicité, cependant. Les commentaires sont remplacés par un seul espace dans la “phase de traduction”, ce qui se produit avant l’parsing de la directive de prétraitement. (Section 6.10 pour plus de détails).

VC ++ et le compilateur GNU C suivent tous deux ce paradigme: les autres compilateurs ne sont peut-être pas conformes s’ils sont plus anciens, mais s’ils sont compatibles C99, vous devriez être en sécurité.

Comme décrit dans cette description collégiale des phases de traduction dans le standard C99, la suppression des commentaires (ils sont remplacés par un seul espace) intervient dans la phase de traduction 3, tandis que les directives de prétraitement sont traitées et les macros développées en 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 les commentaires sont supprimés et remplacés par un caractère d’espacement avant que les directives de prétraitement ne soient traitées et que les macros développées ne soient pas différentes.

Là encore, le standard C ++ a ces deux phases dans le même ordre.

En ce qui concerne la manière dont les commentaires ‘ // ‘ doivent être traités, la norme C99 le dit (6.4.9 / 2):

Sauf dans une constante de caractère, un littéral de chaîne ou un commentaire, les caractères // introduisent un commentaire qui inclut tous les caractères multi-octets jusqu’au prochain caractère de nouvelle ligne, mais sans l’inclure.

Et le standard C ++ dit (2.7):

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

Donc, votre premier exemple est clairement une erreur de la part de ce traducteur – le ‘ ; Le caractère après le foo(a) doit être conservé lorsque la macro foo() est développée – les caractères de commentaire ne doivent pas faire partie du “contenu” de the foo() macro the foo() .

Mais comme vous êtes confronté à un traducteur bogué, vous souhaiterez peut-être changer la définition de la macro pour:

 #define foo(x) /* junk */ 

contourner le bogue.

Cependant (et je suis en train de dériver du sujet ici …), puisque l’épissage des lignes (barres obliques inverses juste avant une nouvelle ligne) se produit avant que les commentaires ne soient traités, vous pouvez rencontrer quelque chose comme ce morceau de code désagréable:

 #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 quiconque l’a écrit.

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

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

Selon que votre compilateur traite ou non les sortinggraphes par défaut (les compilateurs sont supposés le faire, mais les sortinggraphes surprennent presque tout le monde qui les traverse, certains compilateurs décident de les désactiver par défaut), quel que soit le comportement, bien sûr.

Selon MSDN , les commentaires sont remplacés par un seul espace dans la phase de jetonisation, ce qui se produit avant la phase de prétraitement au cours de laquelle les macros sont développées.

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 est toujours sûr à utiliser. Par exemple:

 if (some condition) foo(x); 

ne jettera jamais une erreur de compilation, que foo soit défini ou non sur une expression.

 #ifdef _TEST_ #define _cerr cerr #else #define _cerr / ## / cerr #endif 
  • travaillera sur certains compilateurs (VC ++). Lorsque _TEST_ n’est pas défini,

    _cerr …

    sera remplacé par la ligne de commentaires

    // cerr …

Il me semble rappeler que la conformité nécessite trois étapes:

  1. bande
  2. développer des macros
  3. se déshabiller

La raison en est que le compilateur peut accepter directement les fichiers .i.