web-dev-qa-db-fra.com

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

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);

É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? Élidez d'abord les commentaires ou développez d'abord les macros?

54
Phil Miller

Malheureusement, l'original spécification ANSI C exclut spécifiquement toutes les fonctionnalités du préprocesseur dans la section 4 ("Cette spécification ne décrit que le langage C. Elle ne fait aucune provision pour la bibliothèque ou le préprocesseur.").

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

VC++ et GNU C Compiler suivent tous les deux ce paradigme - d'autres compilateurs peuvent ne pas être conformes s'ils sont plus anciens, mais s'il est conforme C99, vous devriez être en sécurité.

31
Reed Copsey

Comme décrit dans cette description copiée-collée des phases de traduction dans la norme C99, la suppression des commentaires (ils sont remplacés par un seul espace blanc) se produit dans la phase de traduction 3, tandis que les directives de prétraitement sont gérées et les macros sont développés dans la phase 4.

Dans la norme C90 (que je n'ai que sur papier, donc pas de copier-coller), ces deux phases se produisent 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 sont supprimés et remplacés par un seul caractère d'espace avant que les directives de prétraitement ne soient traitées et que les macros développées ne soient pas différentes.

Encore une fois, la norme C++ a ces 2 phases se produisent dans le même ordre.

En ce qui concerne la façon dont les commentaires "//" Doivent être traités, la norme C99 dit ceci (6.4.9/2):

Excepté 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 caractère de nouvelle ligne suivant, mais non inclus.

Et la norme 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 caractère ';' Après la 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 la macro the foo().

Mais comme vous êtes confronté à un traducteur de bogues, vous souhaiterez peut-être modifier la définition de la macro en:

#define foo(x) /* junk */

pour contourner le bogue.

Cependant (et je dérive du sujet ici ...), puisque l'épissage de ligne (barres obliques inverses juste avant une nouvelle ligne) se produit avant le traitement des commentaires, 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 celui qui l'a écrit.

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

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

Selon que votre compilateur utilise par défaut le traitement trigraphs ou non (les compilateurs sont censés le faire, mais comme les trigraphs surprennent presque tous ceux qui les parcourent, certains compilateurs décident de les désactiver par défaut), vous pouvez ou pouvez ne pas obtenir le comportement que vous voulez - quel que soit le comportement, bien sûr.

10
Michael Burr

Selon MSDN , les commentaires sont remplacés par un seul espace dans la phase de tokenisation, qui se produit avant la phase de prétraitement où les macros sont développées.

5
Jim Lewis

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 lancera jamais d'erreur de compilation, que foo soit défini ou non sur une expression.

4
Vitali
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
  • fonctionnera sur certains compilateurs (VC++). Quand _TEST_ n'est pas défini,

    _cerr ...

    sera remplacé par la ligne de commentaire

    // cerr ...

2
Mike Peeler

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

  1. bande
  2. développer les macros
  3. se déshabiller à nouveau

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

1
Joshua