делать {…} while (0) - для чего это нужно? [Дубликат]

284

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

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

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

  • 0
    Посмотрите на этот вопрос .
  • 10
    На самом деле, это не дубликат, так как связанный вопрос не является определенным для определения. Легко сравнить оба ответа, чтобы утверждать, что это не дубликат.
Показать ещё 4 комментария
Теги:
loops

5 ответов

383
Лучший ответ

Это единственная конструкция в 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
    ....
  • 4
    Не #define FOO(x) if(true){ foo(x); bar(x); } else void(0) также работает, даже если это намного уродливее?
  • 2
    Но как насчет #define FOO(x) foo(x), bar(x) поэтому используйте запятую вместо точки с запятой. Разве это не будет работать нормально, если еще?
Показать ещё 15 комментариев
85

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

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);
  • 13
    э-э, включите его в метод и используйте ранний возврат.
  • 12
    или ... просто используйте do {} while (0).
Показать ещё 4 комментария
61

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

#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.

  • 2
    Очень четкий ответ. +1
  • 1
    Понятно понятно. ;)
17

Интересно отметить следующую ситуацию, когда 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;
}
  • 4
    Это расширение GCC. В C ++ 11 вы можете сделать то же самое с лямбдой.
-4

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

do {
    char c = read_input();

    process_input(c);
} while (c != 'Q');
  • 1
    Он также доступен в C #, в котором нет макросов. Я не уверен, почему кто-то проголосовал против этого ответа, но я вижу его как почти правильный ответ, за исключением того, что он пропустил явное «0» в условии while. Пожалуйста, люди, если вы отрицаете чей-либо ответ, пожалуйста, прокомментируйте.
  • 16
    Я не был тем, чтобы понизить голос; однако вопрос очень специфичен, и ответ в целом верен, но вне контекста.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню