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');