2010-03-04 24 views
20

Possibili duplicati:
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 blockCosa fa "do {...} while (0)" esattamente nel codice del kernel?

ho visto un sacco di usi come questo, in precedenza ho pensato che il programmatore ha voluto uscire da un blocco di codice facilmente. Perché abbiamo bisogno di un ciclo do {...} while (0) qui? Stiamo cercando di dire qualcosa al compilatore?

Per esempio nel 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) 
+0

Penso che tu abbia ragione. Questo crea il blocco da cui puoi uscire. Inoltre crea un altro frame nello stack, ma nella maggior parte dei casi verrà semplicemente ottimizzato. Per ulteriori indizi date un'occhiata alle definizioni di ia64_ * Potrebbero essere macro che hanno dichiarazioni di rottura o qualche altro tipo di presa in giro. – Vlad

+2

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

+0

O li fai tutti o non ne fai nessuno –

risposta

2

sembra che sia lì solo per scoping. E 'simile a:

if (true) 
{ 
    // Do stuff. 
} 

modificare

Io non lo vedo nel tuo esempio, ma è possibile che uno di quelli chiama la funzione è in realtà una macro, nel qual caso c'è una differenza fondamentale tra do/while (0) e if (true), ovvero che il primo consente continue e break.

+0

Se è solo per l'ambito, perché ha anche il 'do' e il 'while (0)' o nel caso di questa risposta 'se è vero)'? Perché non lasciare che le parentesi graffe di apertura e chiusura stiano da sole? – semaj

+0

La risposta è che non è * solo * per l'ambito. AndreyT e altri hanno spiegato il resto. –

23

Viene sempre utilizzato nei macro in modo che sia necessario un punto e virgola dopo una chiamata, proprio come quando si chiama una funzione regolare.

Nel tuo esempio, si deve scrivere

__local_irq_save(1); 

mentre

__local_irq_save(1) 

si tradurrebbe in un errore circa un punto e virgola mancante. Questo non succederebbe se il do non ci fosse. Se si trattava solo di scoping, basterebbe una semplice coppia di parentesi graffe.

+0

+1 Questa è una spiegazione migliore. –

+0

Affascinante. Questo è un trucco a portata di mano! – Toji

+0

Affascinante, una specie di. Suggerirei comunque una funzione inline su una tale macro in qualsiasi momento (se usi C++), il che ti renderà libero il punto e virgola :) – OregonGhost

22

Permette il codice appaia qui:

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

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

se semplicemente usato un { .. } si otterrebbe

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

Il resto non apparterrebbe a qualsiasi if più, perché il punto e virgola sarebbe essere la prossima dichiarazione e separare il else dal precedente if. Si sarebbe verificato un errore di compilazione.

+0

Anche un valido motivo. D'altra parte, io uso sempre parentesi graffe per le istruzioni di controllo del flusso, in modo che ciò non possa accadere. Un errore del compilatore sarebbe quindi accettabile per me. Sarebbe peggio se il resto fosse separato dal if * senza * un errore di compilazione;) – OregonGhost

+0

OregonGhost: Non c'è sempre un errore di sintassi. 'se (permesso) se (! Appropriato) __set_error (NOT_RIGHT_NOW); else __do_root_action (WIN); ' Con la macro' __set_error' usando 'do {...} while (0)', questo funziona, se si usa solo '{...}' il resto è effettivamente collegato al ' if (consentito) 'invece di' if (! appropriato) '. Oops. –

+0

@ Michael Speer: non è vero. Se '__set_error' usa' {} ', allora'; 'dopo' __set_error' terminerà sia 'if'-s che' else' diventerà orfano - errore di sintassi. Ho sentito parlare del potenziale problema con 'else' associato male, ma non riesco a trovare un esempio adeguato. Forse non è davvero possibile, solo una leggenda metropolitana. Chiunque? – AnT

11

Lo scopo di do{ ... } while(0) costrutto è quello di trasformare un gruppo di istruzioni in una singola istruzione composto che può essere risolto con una ;. Vedete, in linguaggio C il costrutto do/while ha una proprietà strana e inusuale: anche se "funziona" come un'istruzione composta, si aspetta uno ; alla fine. Nessun altro costrutto composto in C ha questa proprietà.

A causa di questa proprietà, è possibile utilizzare do/while di scrivere le macro più istruzioni, che possono essere utilizzate in modo sicuro come funzioni "normali", senza preoccuparsi cosa c'è dentro la macro, come nel seguente esempio

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

Il la risposta è già stata data (quindi la macro forza un ; quando viene chiamata), ma un altro uso di questo tipo di affermazione che ho visto: consente di chiamare la pausa ovunque nel "loop", terminando anticipatamente se necessario. Essenzialmente un "goto" che i tuoi colleghi programmatori non ti ucciderebbero per.

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

Si noti che questo è ancora abbastanza confuso, quindi non incoraggio il suo utilizzo.

2

Fa uso della macro come una vera e propria dichiarazione o chiamata di funzione.

Una dichiarazione è o { expression-list } o expression; in modo che pone un problema al momento di definire le macro che hanno bisogno di più di un'espressione, perché se si utilizza { } allora si verificherà un errore di sintassi se il chiamante della macro ragionevolmente aggiunge un ; prima un altro

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

Se f() è un singolo macro affermazione, bene, ma se si tratta di una macro e qualcosa di complicato? Finisci con if(...) { s1; s2; }; else ... e questo non funziona.

Quindi lo scrittore della macro deve trasformarlo in una funzione reale, racchiudere il costrutto in una singola istruzione o utilizzare un'estensione di GNU.

Il modello do .. while(0) è l'approccio "wrap the construct".