Do {...} while (0) - для чего это полезно?

Возможный дубликат:
Почему иногда существуют бессмысленные операторы do/while и if/else в макросах C/С++?

Я видел это выражение уже более 10 лет. Я пытался подумать, для чего это полезно. Поскольку я вижу это в основном в #defines, я предполагаю, что он хорош для внутреннего объявления переменной области видимости и для использования разрывов (вместо gotos.)

Это хорошо для чего-нибудь еще? Вы используете его?

Ответ 1

Это единственная конструкция в C, которую вы можете использовать для #define операции многоступенчатых операций, после этого поставить точку с запятой и использовать ее в инструкции if. Пример может помочь:

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

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

Даже использование фигурных скобок не помогает:

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

Использование этого в операторе if потребует, чтобы вы опустили точку с запятой, которая является нелогичной:

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

Если вы определяете FOO следующим образом:

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

то синтаксически корректно следующее:

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

Ответ 2

Это способ упростить проверку ошибок и избежать глубоких вложенных if. Например:

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);

Ответ 3

Это помогает сгруппировать несколько операторов в один, так что функционально-подобный макрос может фактически использоваться как функция. Предположим, у вас есть:

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

и вы делаете:

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

затем это расширяется до:

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

Обратите внимание, что второй вызов bar(n) больше не является частью оператора if.

Оберните оба в do { } while(0), и вы также можете использовать макрос в выражении if.

Ответ 4

Интересно отметить следующую ситуацию, когда do {} while (0) loop не будет работать для вас:

Если вы хотите, чтобы функционально-подобный макрос возвращал значение, вам понадобится выражение : ({stmt; stmt;}) вместо 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;
}

Ответ 5

В общем случае do/while хорош для любой конструкции цикла, где один цикл должен выполняться хотя бы один раз. Эмуляция такого типа может происходить через прямой цикл while или даже for, но часто результат немного менее изящный. Я признаю, что конкретные приложения этого шаблона довольно редки, но они существуют. Один из них, который приходит на ум, представляет собой консольное приложение на основе меню:

do {
    char c = read_input();

    process_input(c);
} while (c != 'Q');