fai {…} mentre (0) nelle macro

fai {…} mentre (0) nelle macro

Se sei un programmatore C, devi avere familiarità con le macro. Sono potenti e possono aiutarti a facilitare il tuo lavoro se usati correttamente. Tuttavia, se non definisci attentamente le macro, potrebbero morderti e farti impazzire. In molti programmi C, potresti vedere una definizione di macro speciale che potrebbe non sembrare così semplice. Ecco un esempio:

#define __set_task_state(tsk, state_value) \
    do { (tsk)->state = (state_value); } while (0)

Esistono molte macro di questo tipo che utilizzano do{…}while(0) nei kernel Linux e in altre librerie C popolari. A cosa serve questa macro? Robert Love di Google (in precedenza ha lavorato allo sviluppo del kernel Linux) ci dà la risposta.

do{…}while(0) è l'unico costrutto in C che ti consente di definire macro che funzionano sempre allo stesso modo, in modo che un punto e virgola dopo la tua macro abbia sempre lo stesso effetto, indipendentemente da come viene utilizzata la macro (con particolare enfasi sulla questione dell'annidamento della macro in un se senza parentesi graffe). Ad esempio:
Più tardi puoi chiamare:

foo(wolf);

Questo sarà esteso a:

bar(wolf); baz(wolf);

Questo è l'output atteso. Quindi vediamo se abbiamo:

if (!feral)
    foo(wolf);

L'espansione potrebbe non essere quella che ti aspetti. L'espansione può essere:

if (!feral)
    bar(wolf);
baz(wolf);

Non è possibile scrivere macro multiistruzione che fanno la cosa giusta in tutte le situazioni. Non puoi fare in modo che le macro si comportino come funzioni, senza fare/mentre(0).

Se ridefiniamo la macro con do{…}while(0), vedremo:

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

Ora, questa affermazione è funzionalmente equivalente alla prima. Il do assicura che la logica all'interno delle parentesi graffe venga eseguita, mentre il while(0) assicura che ciò accada solo una volta. Come senza il ciclo. Per l'istruzione if precedente, sarà:

if (!feral)
    do { bar(wolf); baz(wolf); } while (0);

Semanticamente, è lo stesso di:

if (!feral) {
    bar(wolf);
    baz(wolf);
}

Potresti rientrare, perché non racchiudere la macro tra parentesi graffe? Perché avere anche la logica do/while(0)? Ad esempio, definiamo la macro con parentesi graffe:

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

Questo va bene per l'istruzione if sopra, ma se abbiamo l'istruzione seguente:

if (!feral)
    foo(wolf);
else
    bin(wolf);

Il codice espanso sarà :

if (!feral) {
    bar(wolf);
    baz(wolf);
};
else
    bin(wolf);

Questo è un errore di sintassi.

In conclusione, le macro in Linux e altre basi di codice racchiudono la loro logica in do/while(0) perché assicura che la macro si comporti sempre allo stesso modo, indipendentemente da come i punti e virgola e le parentesi graffe vengono utilizzati nel codice di richiamo.

Fonte http://www.pixelstech.net/