2010-04-13 3 views
4

je jouais avec une macro pour activer/désactiver des traces quand je suis sorti avec le code suivant lorsque la macro est désactivé:opérateur Parenthèses en C. Quel est l'effet dans le code suivant

int main() 
{ 

    ("Hello world"); 

} 

Ce code est valide et j'ai obtenu l'effet désiré (rien ne se passe quand la macro est désactivée) mais je n'ai pas compris ce qui se passe exactement. Le compilateur voit-il les parenthèses comme une déclaration de méthode "sans nom"?

Pour le rendre plus clair le code est:

#ifdef TRACE 

    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf 
else 

    #define trace 
#endif 

int main() 
{ 

    trace("Hello world"); 

} 

Merci à l'avance.

+2

Un point un peu délicat avec des macros multi-instructions comme ceci est qu'ils ne fonctionnent pas comme ils peuvent apparaître avec des choses comme instructions 'if' (vous ne devez pas dire quelque chose comme' if (condition) TRACE ("mauvais"); ') - peut-être pourriez-vous corriger ceci en utilisant' && 'à la place du point-virgule (?) - ou soyez prudent en utilisant la macro;) –

Répondre

18

Si le nom de la fonction est absent, comme dans votre premier exemple, ce n'est pas un "opérateur parenthèse". C'est simplement un élément syntaxique d'une expression qui modifie l'association entre opérateurs et opérandes. Dans ce cas, cela ne fait rien. Qu'est-ce que vous avez est juste une expression

"Hello world"; 

qui évalue à une valeur de type char *, et cette valeur est ignorée. Vous pouvez entourer cette expression dans une paire redondante de ()

("Hello world"); 

qui ne changera rien.

exactement de la même façon que vous pouvez écrire

(5 + 3); 

au milieu de votre code et d'obtenir une expression qui évalue la valeur 8, qui est immédiatement mis au rebut.

Habituellement, les compilateurs ne génèrent aucun code pour les instructions d'expression qui n'ont pas d'effets secondaires. En fait, dans le langage C, le résultat de chaque instruction d'expression est supprimé, de sorte que les seules instructions d'expression qui ont un sens sont des instructions d'expression avec des effets secondaires. Les compilateurs sont normalement assez bons pour détecter des déclarations sans effet et les rejeter (parfois avec un avertissement).

L'avertissement pourrait être gênant, l'écriture si les instructions d'expression effectless comme

"Hello world"; 

pourrait ne pas être une bonne idée. compilateurs généralement reconnaître une distribution à void comme une demande de ne pas générer cet avertissement

(void) "Hello world"; 

Alors vous pourriez envisager de redéfinir votre macro en conséquence.

Bien sûr, en utilisant la technique trace ci-dessus, vous devez vous rappeler que si vous mettez quelque chose qui ne ont un effet secondaire comme argument pour votre macro

trace("%d\n", i++); 

alors en forme « désactivé » il se présentera comme suit

("%d\n", i++); 

(deux sous-expressions, chaînées par une virgule dans une expression). L'effet secondaire de l'incrémentation i persiste dans ce cas, il n'est pas désactivé.Le tout est équivalent à la plaine

i++; 

Aussi, si vous utilisez un appel de fonction comme un argument

trace(get_trace_name()); 

la forme « disabled » regardera comme

(get_trace_name()); 

et le compilateur peut ne pas être assez intelligent pour se rendre compte que l'appel à get_trace_name() doit être ignoré. Alors, soyez prudent lorsque vous utilisez votre macro. Évitez les arguments avec effets secondaires, évitez les arguments avec appels de fonction, à moins, bien sûr, que vous ayez l'intention de préserver les effets secondaires lors de la désactivation du suivi réel.

+0

L'autre chose est qu'il est important d'éviter les effets secondaires dans les arguments de 'trace', mais c'est une bonne idée quand même. –

+0

@ Fellows décernés: Merci. Je viens d'écrire un peu plus à ce sujet :) – AnT

+0

C'est une explication approfondie, merci de passer le temps à l'écrire :) – Andre

2
("Hello world"); 

est une expression renvoyant un pointeur constant vers une chaîne. Cette valeur n'est pas consommée.

Parenthèses ont aucun rôle spécifique et vous pouvez les omettre:

"Hello world"; 
+0

Mais que se passe-t-il si j'ai un printf plus élaboré? Par exemple si au lieu de "Hello World" j'avais:
int main
{ int x; char * str = "Bonjour tout le monde"; trace ("Test,% d,% s", x, str); } la trace serait remplacée par rien (cas de trace désactivée) en laissant l'expression suivante: ("Test,% d,% s", x, str); qui compile et ne fait rien. – Andre

+1

Une séquence d'expressions séparées par ',' est correcte dans C. La valeur de l'expression résultante est la valeur de la dernière expression de la séquence. – mouviciel

+0

Donc tout sera évalué mais le "str" ​​sera la valeur retournée? L'effet secondaire de cette approche est donc que le code est évalué sans nécessité. – Andre

0
#ifdef TRACE 
    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf 
#else 
    #define trace 
#endif 

int main { 
    trace("Hello world"); 
} 

La façon dont les macros fonctionnent en C est que le compilateur (essentiellement) * faire une littérale remplacer de l'identifiant .

Donc, dans votre cas, il y a deux options en fonction de la valeur du #IFDEF

trace("Hello world");

peut devenir

  1. printf("%s %d -> ",__FILE__, __LINE__);printf("Hello world");

ou

  1. ("Hello world");

La première option est une séquence de code valide de C, qui se compose de deux états printf. La deuxième option est une séquence de code C valide qui consiste en une chaîne (char *) à l'intérieur des accolades inutiles.

3

Que cela fonctionne ou non peut dépendre exactement de ce que vous passez en tant qu'arguments de la macro (voir le problème des effets secondaires mentionné par AndreyT). Dans ce cas, c'est bénin. Cependant ce qui suit est probablement plus sûr car il se traduira par aucun texte étant inséré lorsque la macro est traitée et TRACE n'est pas défini:

#ifdef TRACE 
    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf 
#else 
    #define trace(...) 
#endif 

en supposant que votre compilateur prend en charge les macros variadique. Dans le cas contraire ce qui suit serait peut-être une meilleure définition:

#ifdef TRACE 
    #define trace(fmt, ...) printf("%s %d -> " fmt, __FILE__, __LINE__, __VA_ARGS__) ; 
#else 
    #define trace(...) 
#endif 

Notez que l'absence d'une virgule entre "%s %d -> " et fmt est délibéré et nécessaire.Notez également que l'argument doit être être une constante chaîne littérale pour que la concaténation de chaîne littérale adjacente se produise - une variable de n'importe quel type générerait une erreur, mais bad practice pour utiliser une variable pour un spécificateur de format dans tous les cas.

+0

Vous avez des problèmes avec le C99 strict là-bas. Apparemment, vous devriez mettre une virgule après la chaîne lorsque vous l'appelez dans ce cas. OMI c'est quelque part où la spécification C99 est fausse. –

+0

Fellows @Donal: Êtes-vous sûr? La contrainte dans ce cas est que 'fmt' * doit * être une chaîne littérale afin de profiter de la concaténation littérale de chaîne adjacente; J'aurais dû être clair sur ce point. Dans tous les cas, il est recommandé de n'utiliser que des constantes littérales de chaîne en tant que spécificateurs de format. Il permet le code comme 'trace (" var =% d ", var);' pour sortir 'main.c 10 -> var = 24' par exemple. Peut-être que je ne comprends pas votre point de vue sur C99; pouvez-vous citer la référence ISO pour cette assertion? – Clifford

+0

Mise à jour, à partir de l'avant-projet N1905 = 05-0165 (parce que c'est gratuit) * 2.3.4 Littéraux de chaînes, paragraphe 3 "En phase de traduction 6 (2.1), les chaînes littérales adjacentes sont concaténées" *. Je me rends compte que le projet de travail n'est pas définitif, mais il semble invraisemblable que cet aspect de C et C++ de longue date change. – Clifford

0

Si votre compilateur prend en charge C99 (ou que vous utilisez gcc qui avait cette fonction plus tôt), vous pouvez utiliser des macros variadique:

#ifdef TRACE 
#define trace(...) printf("%s %d -> ",__FILE__, __LINE__);printf(__VA_ARGS__) 
#else 
#define trace(...) 
#endif 

Cela évite les problèmes que vous pouvez obtenir des effets secondaires dans les arguments. Si vous avez un compilateur C89 strict, vous devez juste éviter les effets secondaires ...

+0

VC++ 2005 et versions ultérieures prennent également en charge les macros variadiques, même si elles ne prennent pas en charge C99.Vous n'avez pas non plus besoin du second printf() si vous concaténez la chaîne de format (voir mon article précédent); ceci rend la macro utilisable * n'importe où * un appel de fonction est valide. – Clifford