do { ... } while (0) - do czego to służy? [duplikat]

Możliwy duplikat:
Dlaczego w makrach C/C++ występują czasami bezsensowne wyrażenia do/while I if/else?

Widzę to wyrażenie od ponad 10 lat. Zastanawiam się, do czego to służy. Ponieważ widzę to głównie w # defines, zakładam, że jest dobre dla deklaracji zmiennej Inner scope i dla używania przerw (zamiast gotos.) Czy jest dobry na coś jeszcze? Używasz go?
 271
Author: Community, 2008-11-03

5 answers

Jest to jedyna konstrukcja w C, której możesz użyć do #define operacji wielostanowiskowej, umieścić średnik PO i nadal używać w instrukcji if. Przykład może pomóc:

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

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

Nawet używanie szelek nie pomaga:

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

Użycie tego wyrażenia w if wymagałoby pominięcia średnika, który jest przeciwny dointuitywny:

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

Jeśli zdefiniujesz FOO tak:

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

Wtedy następująca jest poprawna składniowo:

if (condition)
    FOO(x);
else
    ....
 403
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-03 09:24:33

Jest to sposób na uproszczenie sprawdzania błędów i unikanie głębokiego zagnieżdżania if. na przykład:

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);
 89
Author: Jere.Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-02 23:48:54

Pomaga grupować wiele poleceń w jedno, tak aby makro podobne do funkcji mogło być użyte jako funkcja. Załóżmy, że masz

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

I robisz

void foobar(int n){
  if (n)
     FOO(n);
}

Następnie to rozszerza się do

void foobar(int n){
  if (n)
     foo(n);bar(n);
}

Zauważ, że drugie wywołanie (bar (n)) nie jest już częścią instrukcji if.

Zawiń oba w do{}while(0), a możesz również użyć makra w instrukcji if.

 64
Author: Martin v. Löwis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-02 21:39:51

Warto zwrócić uwagę na następującą sytuację, w której pętla do {} while (0) nie będzie działać dla Ciebie:

Jeśli chcesz mieć makro podobne do funkcji, które zwraca wartość, będziesz potrzebował wyrażenia instrukcji : ({stmt; stmt;}) zamiast do {} while (0):


#include <stdio.h>

#define log_to_string1(str, fmt, arg...) \
    do { \
        sprintf(str, "%s: " fmt, "myprog", ##arg); \
    } while (0)

#define log_to_string2(str, fmt, arg...) \
    ({ \
        sprintf(str, "%s: " fmt, "myprog", ##arg); \
    })

int main() {
        char buf[1000];
        int n = 0;

        log_to_string1(buf, "%s\n", "No assignment, OK");

        n += log_to_string1(buf + n, "%s\n", "NOT OK: gcc: error: expected expression before 'do'");

        n += log_to_string2(buf + n, "%s\n", "This fixes it");
        n += log_to_string2(buf + n, "%s\n", "Assignment worked!");
        printf("%s", buf);
        return 0;
}
 17
Author: ubuntu-fanboy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-07 16:34:43

Ogólnie, do/while jest dobre dla każdego rodzaju konstrukcji pętli, gdzie należy wykonać pętlę przynajmniej raz. Możliwe jest naśladowanie tego rodzaju pętli poprzez prostą while lub nawet pętlę for, ale często wynik jest nieco mniej elegancki. Przyznam, że konkretne zastosowania tego wzoru są dość rzadkie, ale istnieją. Jednym, który przychodzi na myśl, jest aplikacja konsolowa oparta na menu:

do {
    char c = read_input();

    process_input(c);
} while (c != 'Q');
 -4
Author: Daniel Spiewak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-02 21:39:02