Делать { ... } while (0) - для чего это нужно?
Возможный дубликат:
Почему в макросах C/C++ иногда есть бессмысленные операторы do / while и if / else?
Я видел это выражение уже более 10 лет. Я пытался думать, для чего это хорошо. Поскольку я вижу это в основном в #defines, я предполагаю, что это хорошо для объявления переменных внутренней области видимости и для использования разрывов (вместо gotos.)
Это хорошо для чего-то еще? Вы используете это?
5 ответов
Это единственная конструкция в 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
....
Это способ упростить проверку ошибок и избежать глубокого вложенного 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) не будет работать для вас:
Если вам нужен макрос типа функции, который возвращает значение, тогда вам понадобится выражение оператора: ({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');