2010-03-04 7 views
20

Doublons possibles:
What’s the use of do while(0) when we define a macro?
Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?
C multi-line macro: do/while(0) vs scope blockQue fait exactement "do {...} while (0)" dans le code du noyau?

J'ai vu beaucoup d'usages comme celui-ci, auparavant je pensais que le programmeur a voulu sortir d'un bloc de code facilement. Pourquoi avons-nous besoin d'une boucle do {...} while (0) ici? Essayons-nous de dire quelque chose au compilateur?

Par exemple, dans le noyau Linux 2.6.25, include/asm-ia64/system.h

/* 
* - clearing psr.i is implicitly serialized (visible by next insn) 
* - setting psr.i requires data serialization 
* - we need a stop-bit before reading PSR because we sometimes 
* write a floating-point register right before reading the PSR 
* and that writes to PSR.mfl 
*/ 
#define __local_irq_save(x)   \ 
do {     \ 
    ia64_stop();    \ 
    (x) = ia64_getreg(_IA64_REG_PSR); \ 
    ia64_stop();    \ 
    ia64_rsm(IA64_PSR_I);   \ 
} while (0) 
+0

Je pense que vous avez raison. Cela crée le bloc dont vous pouvez sortir. En outre, il crée un autre cadre sur la pile, mais dans la plupart des cas, il sera simplement optimisé. Pour plus d'indices, jetez un oeil aux définitions de ia64_ * Il pourrait s'agir de macros qui ont des instructions break ou d'autres types de moquerie. – Vlad

+2

http://stackoverflow.com/questions/1067226/c-multi-line-macro-do-while0-vs-scope-block –

+0

Vous les faites tous ou vous n'en faites aucun –

Répondre

2

On dirait qu'il est là juste pour cadrage. Il est similaire à:

if (true) 
{ 
    // Do stuff. 
} 

modifier

Je ne vois pas dans votre exemple, mais il est possible que l'un de ces appels de fonction est en fait une macro, dans ce cas, il y a une différence essentielle entre fais/while (0) et if (true), ce qui est que le premier permet continue et break.

+0

Si c'est juste pour la portée, pourquoi même avoir le 'faire' et le 'tant que (0)' ou dans le cas de cette réponse 'si vrai)'? Pourquoi ne pas laisser les accolades d'ouverture et de fermeture se suffire à elles-mêmes? – semaj

+0

La réponse est que ce n'est pas * juste * pour la portée. AndreyT et d'autres ont expliqué le reste. –

23

Il est toujours utilisé dans les macros de sorte qu'un point-virgule est requis après un appel, tout comme lorsque vous appelez une fonction normale.

Dans votre exemple, vous devez écrire

__local_irq_save(1); 

tout

__local_irq_save(1) 

entraînerait une erreur d'un point-virgule manquant. Cela ne se produirait pas si le faire n'était pas là. S'il s'agissait juste d'une portée, une simple paire d'accolades suffirait.

+0

+1 C'est une meilleure explication. –

+0

Fascinant. C'est un truc pratique! – Toji

+0

Fascinant, genre de. Je recommanderais une fonction inline sur une telle macro à tout moment (si vous utilisez C++), ce qui vous donne l'exigence du point-virgule gratuitement :) – OregonGhost

22

Il permet le code apparaisse ici:

if(a) __local_irq_save(x); else ...; 

// -> if(a) do { .. } while(0); else ...; 

S'ils ont simplement utilisé un { .. } vous obtiendrez

if(a) { ... }; else ...; 

L'autre n'appartenir à une plus if, parce que le point-virgule serait être la déclaration suivante et séparer le else du précédent if. Une erreur de compilation se produirait.

+0

Aussi une raison valable. D'autre part, j'utilise toujours des accolades pour les instructions de flux de contrôle, afin que cela ne puisse pas arriver. Une erreur de compilation serait donc acceptable pour moi. Ce serait pire si l'autre était séparé de if * sans * une erreur de compilation;) – OregonGhost

+0

OregonGhost: Il n'y a pas toujours d'erreur de syntaxe. 'si (autorisé) si (! Approprié) __set_error (NOT_RIGHT_NOW); Avec __do_root_action (WIN); ' Avec la macro' __set_error' utilisant 'do {...} while (0)', cela fonctionne, si vous utilisez juste {...} 'l'autre est attaché à' ' if (allowed) 'au lieu de' if (! appropriate) '. Oups. –

+0

@Michael Speer: Pas vrai. Si '__set_error' utilise' {} ', alors'; 'après' __set_error' se terminera à la fois 'if'-s et' else' deviendront orphelins - erreur de syntaxe. J'ai entendu parler de la question potentielle avec «autre» mal associé, mais je ne peux pas trouver un bon exemple. Peut-être que ce n'est pas vraiment possible, juste une légende urbaine. N'importe qui? – AnT

11

Le but de do{ ... } while(0) construction est de transformer un groupe de déclarations en une seule instruction composée qui peut être obturé par une ;. Vous voyez, en langage C, la construction do/while a une propriété bizarre et inhabituelle: même si elle "fonctionne" comme une instruction composée, elle attend un ; à la fin. Aucune autre construction de composé en C n'a cette propriété.

En raison de cette propriété, vous pouvez utiliser do/while pour écrire des macros multi-états, qui peuvent être utilisés en toute sécurité comme des fonctions « ordinaires » sans se soucier ce qui est à l'intérieur de la macro, comme dans l'exemple suivant

if (/* some condition */) 
    __local_irq_save(x); /* <- we can safely put `;` here */ 
else 
    /* whatever */; 
5

Le la réponse a déjà été donnée (ainsi la macro force un ; quand on l'appelle), mais une autre utilisation de ce genre d'instruction que j'ai vu: elle permet d'appeler n'importe quelle coupure dans la "boucle", se terminant tôt si nécessaire. Essentiellement un "goto" que vos collègues programmeurs ne vous tueraient pas pour.

do { 
    int i = do_something(); 
    if(i == 0) { break; } // Skips the remainder of the logic 
    do_something_else(); 
} while(0); 

Notez que cela est encore assez confus, donc je ne favorise pas son utilisation.

2

Il utilise la macro comme une instruction réelle ou un appel de fonction.

Une déclaration est soit { expression-list } ou expression; pour que pose un problème lors de la définition des macros qui ont besoin de plus d'une expression, parce que si vous utilisez { } alors une erreur de syntaxe se produit si l'appelant de la macro ajoute tout à fait raisonnablement ; avant un autre.

if(whatever) 
    f(x); 
else 
    f(y); 

Si f() est une macro simple déclaration, très bien, mais si c'est une macro et quelque chose de compliqué? Vous vous retrouvez avec if(...) { s1; s2; }; else ... et cela ne fonctionne pas. Donc, l'auteur de la macro doit alors soit en faire une fonction réelle, envelopper la construction dans une seule instruction, ou utiliser une extension GNU.

Le modèle do .. while(0) est l'approche "wrap the construct".