Qu’est-ce que “do {…} while (0)” fait exactement dans le code du kernel?

Doublons possibles:
Quelle est l’utilisation de do while (0) lorsque nous définissons une macro?
Pourquoi y a-t-il parfois des instructions do / while et if / else sans signification dans les macros C / C ++?
C macro multiligne: bloc do / while (0) vs scope

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

Par exemple, dans le kernel 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) 

Il est toujours utilisé dans les macros, de sorte qu’un point-virgule est requirejs après un appel, comme lors de l’appel d’une fonction standard.

Dans votre exemple, vous devez écrire

 __local_irq_save(1); 

tandis que

 __local_irq_save(1) 

entraînerait une erreur à propos d’un point-virgule manquant. Cela ne se produirait pas si le temps n’était pas là. S’il s’agissait simplement de la scope, une simple paire de corset suffirait.

Cela permet au code d’apparaître ici:

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

S’ils utilisaient simplement un { .. } vous obtiendriez

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

Le rest n’appartiendrait plus à aucun if , car le point-virgule serait la prochaine instruction et séparerait le else du précédent if . Une erreur de compilation se produirait.

Le but de la construction do{ ... } while(0) est de transformer un groupe d’instructions en une instruction composée unique pouvant être terminée par un ; . Vous voyez, en langage C, la construction do/while a une propriété étrange et inhabituelle: même si elle “fonctionne” comme une déclaration composée, elle attend un ; à la fin. Aucune autre construction composée en C n’a cette propriété.

En raison de cette propriété, vous pouvez utiliser do/while pour écrire des macros à instructions multiples, qui peuvent être utilisées en toute sécurité en tant que fonctions “ordinaires” sans se soucier du contenu de la macro, comme dans l’exemple suivant.

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

La réponse a déjà été donnée (donc la macro force a ; lorsqu’elle est appelée), mais une autre utilisation de ce type d’énoncé que j’ai vu auparavant: elle permet d’appeler break dans n’importe quelle partie de la “boucle”, en se terminant tôt si nécessaire. Essentiellement un “goto” pour lequel vos collègues programmeurs ne vous assassineraient pas.

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

Notez que c’est encore assez déroutant, donc je n’encourage pas son utilisation.

On dirait que c’est là juste pour le cadrage. C’est semblable à:

 if (true) { // Do stuff. } 

modifier

Je ne le vois pas dans votre exemple, mais il est possible que l’un de ces appels de fonction soit en fait une macro. Dans ce cas, il existe une différence essentielle entre do / while (0) et if (true), à ​​savoir que le premier permet continue et break .

Il utilise l’acte de macro comme une déclaration ou un appel de fonction réel.

Une instruction est soit { expression-list } ou expression; Cela pose donc un problème lors de la définition de macros nécessitant plus d’une expression, car si vous utilisez { } une erreur de syntaxe se produira si l’appelant de la macro ajoute assez raisonnablement un ; avant un autre.

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

Si f() est une macro d’instruction unique, d’accord, mais que se passe-t-il si c’est une macro et quelque chose de compliqué? Vous vous retrouvez avec if(...) { s1; s2; }; else ... if(...) { s1; s2; }; else ... if(...) { s1; s2; }; else ... et ça ne marche pas.

Ainsi, l’auteur de la macro doit ensuite en faire une fonction réelle, envelopper la construction dans une seule instruction ou utiliser une extension gnu.

Le motif do .. while(0) correspond à l’approche “envelopper la construction”.