macros - C macro multilinea:do / while(0)vs scope block




(2)

Possibili duplicati:
A cosa serve fare while (0) quando definiamo una macro?
Perché ci sono talvolta dichiarazioni do / while e if / else prive di significato in macro C / C ++?
do {...} while (0) a cosa serve?

Ho visto alcune macro C multi-linea che sono avvolte in un ciclo do / while (0) come:

#define FOO \
  do { \
    do_stuff_here \
    do_more_stuff \
  } while (0)

Quali sono i vantaggi (se ce ne sono) di scrivere il codice in questo modo anziché utilizzare un blocco di base:

#define FOO \
  { \
    do_stuff_here \
    do_more_stuff \
  }

Answers

http://bytes.com/groups/c/219859-do-while-0-macro-substitutions

Andrey Tarasevich:

L'idea di usare la versione 'do / while' è di creare una macro che si espanderà in un'istruzione regolare, non in un'istruzione composta. Questo viene fatto per rendere uniforme l'uso delle macro in stile funzione con l'uso di funzioni ordinarie in tutti i contesti.

Si consideri il seguente schema di codice

if (<condition>)
  foo(a);
else
  bar(a);

dove 'foo' e 'bar' sono funzioni ordinarie. Ora immagina che ti piacerebbe sostituire la funzione 'pippo' con una macro della natura precedente

if (<condition>)
  CALL_FUNCS(a);
else
  bar(a);

Ora, se la tua macro è definita secondo il secondo approccio (solo '{' e '}') il codice non verrà più compilato, perché il ramo 'vero' di 'if' è ora rappresentato da un'istruzione composta. E quando metti un ';' dopo questa dichiarazione composta, hai terminato l'intera istruzione "if", rendendo quindi orfano il ramo "else" (da cui l'errore di compilazione).

Un modo per correggere questo problema è ricordare di non mettere ";" dopo macro "invocazioni"

if (<condition>)
  CALL_FUNCS(a)
else
  bar(a);

Questo verrà compilato e funzionerà come previsto, ma non è uniforme. La soluzione più elegante è assicurarsi che la macro si espanda in un'istruzione regolare, non in una composita. Un modo per ottenere ciò è definire la macro come segue

#define CALL_FUNCS(x) \
do { \
  func1(x); \
  func2(x); \
  func3(x); \
} while (0)

Ora questo codice

if (<condition>)
  CALL_FUNCS(a);
else
  bar(a);

compilerà senza problemi.

Tuttavia, nota la piccola ma importante differenza tra la mia definizione di CALL_FUNCS e la prima versione nel tuo messaggio. Non ho messo un ; dopo } while (0) . Mettere un ; alla fine di quella definizione si eliminerebbe immediatamente l'intero punto dell'uso di "do / while" e renderebbe tale macro praticamente equivalente alla versione dell'istruzione composta.

Non so perché l'autore del codice che hai citato nel tuo messaggio originale lo metta ; dopo un while (0) . In questa forma entrambe le varianti sono equivalenti. L'intera idea alla base della versione 'do / while' non è quella di includere questa finale ; nella macro (per i motivi che ho spiegato sopra).


È l'unico costrutto in C che puoi usare per #define un'operazione multistato, mettere un punto e virgola dopo e ancora usare all'interno di un'istruzione if . Un esempio potrebbe aiutare:

#define FOO(x) foo(x); bar(x)

if (condition)
    FOO(x);
else // syntax error here
    ...;

Anche l'utilizzo di parentesi graffe non aiuta:

#define FOO(x) { foo(x); bar(x); }

L'utilizzo di questo in un'istruzione if richiede l'omissione del punto e virgola, il che è controintuitivo:

if (condition)
    FOO(x)
else
    ...

Se definisci FOO in questo modo:

#define FOO(x) do { foo(x); bar(x); } while (0)

allora il seguente è sintatticamente corretto:

if (condition)
    FOO(x);
else
    ....






c macros multiline