Возможный дубликат:
Почему иногда существуют бессмысленные операторы do/while и if/else в макросах C/С++?
Я видел это выражение уже более 10 лет. Я пытался подумать, для чего это полезно. Поскольку я вижу это в основном в #defines, я предполагаю, что он хорош для внутреннего объявления переменной области видимости и для использования разрывов (вместо gotos.)
Это хорошо для чего-нибудь еще? Вы используете его?
Это единственная конструкция в 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
....
#define FOO(x) if(true){ foo(x); bar(x); } else void(0)
также работает, даже если это намного уродливее?
#define FOO(x) foo(x), bar(x)
поэтому используйте запятую вместо точки с запятой. Разве это не будет работать нормально, если еще?
Это способ упростить проверку ошибок и избежать глубоких вложенных if. Например:
do {
// do something
if (error) {
break;
}
// do something else
if (error) {
break;
}
// etc..
} while (0);
Он помогает группировать несколько операторов в один, так что функционально подобный макрос может фактически использоваться как функция. Предположим, что у вас есть
#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.
Интересно отметить следующую ситуацию, когда 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;
}
В общем случае do
/while
хорош для любой конструкции цикла, где один цикл должен выполняться хотя бы один раз. Эмуляция такого типа может происходить через прямой цикл while
или даже for
, но часто результат немного менее изящный. Я признаю, что конкретные приложения этого шаблона довольно редки, но они существуют. Один из них, который приходит на ум, представляет собой консольное приложение на основе меню:
do {
char c = read_input();
process_input(c);
} while (c != 'Q');