2009-07-01 14 views
146

Możliwe duplikaty:
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?
do { … } while (0) what is it good for?C multi-line makro: do/while (0) vs bloku zakres

Widziałem kilka makr multi-linia C które są pakowane wewnątrz pętli do/while (0), takiej jak:

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

Jakie są korzyści (jeśli w ogóle) pisanie kodu w ten sposób, w przeciwieństwie do korzystania z podstawowego bloku:

 
#define FOO \ 
    { \ 
    do_stuff_here \ 
    do_more_stuff \ 
    } 
+4

Duplikaty: http://stackoverflow.com/questions/923822/ i http://stackoverflow.com/questions/154136/ oraz http://stackoverflow.com/questions/257418/ – sth

+0

W rzeczywistości istnieje inny sposób popraw to. ({...}) może zrobić to samo, co zrobić {} podczas (0). Ref: http://www.bruceblinn.com/linuxinfo/DoWhile.html – Nybble

Odpowiedz

212

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

Andrey Tarasevich:

Cały pomysł wykorzystania „zrobić/podczas gdy "wersja polega na stworzeniu makra, które będzie się rozwijało w zwykłą instrukcję, a nie w instrukcję złożoną. Jest to zrobione w celu ujednolicenia makr w stylu funkcji przy użyciu zwykłych funkcji we wszystkich kontekstach.

Rozważmy Kodeksu Poniższe szkic

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

gdzie 'foo' i 'bar' są zwykłe funkcje. Teraz wyobraź sobie, że chcesz chciał zastąpić funkcję „foo” z makro z powyższym charakterze

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

teraz, jeśli makro jest zdefiniowane zgodnie z drugim podejściu (tylko „{” i „} ") kod nie będzie się już kompilował, ponieważ" prawdziwa "gałąź" if " jest teraz reprezentowana przez instrukcję złożoną. A kiedy podasz wstaw ";" po tym złożonym stwierdzeniu zakończyłeś całą instrukcję "if" , osierocając gałąź "else" (stąd błąd kompilacji).

Jednym ze sposobów rozwiązania tego problemu jest pamiętanie, aby nie wstawiać ";" po makro „wywołania”

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

To będzie skompilować i działa zgodnie z oczekiwaniami, ale to nie jest jednolita. Bardziej eleganckie rozwiązanie polega na upewnieniu się, że makro rozwija się w zwykłe oświadczenie , a nie w złożone. Jednym ze sposobów osiągnięcia tego celu jest zdefiniowanie makro następująco

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

Teraz ten kod

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

zostanie skompilowany bez żadnych problemów.

Należy jednak zauważyć niewielką, ale istotną różnicę między moją definicją z CALL_FUNCS a pierwszą wersją w wiadomości. Nie wstawiłem ; po } while (0).Umieszczenie ; na końcu definicji natychmiast zniszczyłoby cały punkt użycia 'do/while' i uczyniłoby z niego jednoznacznie równoważne z wersją złożoną.

Nie wiem, dlaczego autor kodu, który cytowałeś w oryginalnym komunikacie , umieścił ten ; po while (0). W tej postaci oba warianty są równoważne w postaci . Cała idea używania wersji "do/while" nie jest zgodna z włączeniem tego końcowego ; do makra (z powodów, które wyjaśniłem powyżej: ).

+35

Oryginalny post został napisany przeze mnie w 'comp.lang.c'. "bytes.com" najwyraźniej "przywłaszcza" zawartość 'comp.lang.c' bez żadnych odniesień do źródła. – AnT