Possible Duplicate:
Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?
Я наблюдаю это выражение уже более 10 лет. Я долго думал, для чего это нужно. Поскольку я вижу это в основном в #defines, я полагаю, что это хорошо для объявления переменных внутренней области видимости и для использования разрывов (вместо gotos).
Это хорошо для чего-нибудь еще? Вы им пользуетесь?
На самом деле, это не дубликат, поскольку связанный q / a не является специфическим для определения. Легко сравнить оба ответа, чтобы сказать, что это не дубликат.
См. «Decment_used_memory» в строке 53 Redis [ссылка] github.com/antirez/redis-tools/blob/master/zmalloc.c
Дубликат с вопросом, помеченным как возможный дубликат (первая строка сообщения), а не с вопросом, заданным Федерико А. Рампони.
Предлагаемый дубликат предназначен только для использования в макросах. Этот вопрос касается общего использования.
другие варианты этого do { ... } while ((void)0, 0) используются для отключения предупреждений компилятора о "постоянном состоянии".





Это способ упростить проверку ошибок и избежать глубоких вложений if. Например:
do {
// do something
if (error) {
break;
}
// do something else
if (error) {
break;
}
// etc..
} while (0);
э-э, разложите его на метод и используйте ранний возврат.
или ... просто используйте do {} while (0).
Или используйте goto. Нет, серьезно, if (error) goto error; и error: ... ближе к концу кажутся мне более чистой версией, которая выполняет то же самое.
@FireFly О, как goto fail? (конечно, goto не был ошибкой, что на самом деле делает ее еще смешнее)
@WChargin: код «goto fail» в статье, на которую вы ссылаетесь, тоже не прошел бы с «перерывом». Кто-то просто продублировал там строчку. Гото не виноват.
@NiccoloM. "конечно, goto не было ошибкой, что на самом деле делает его еще смешнее"
Я бы рекомендовал использовать это при обработке корней функции, так как вы действительно не знаете, сколько раз вам нужно будет зациклить функцию, чтобы она была близка к 0 и встретила корень, и этот цикл может гарантировать оптимальное время цикла для поиска ближайшего 0,000 ... подход.
не делает его "смешнее", просто делает его совершенно бессмысленным комментарием
Как правило, do / while подходит для любого вида конструкции цикла, где нужно выполнить цикл по меньшей мере один раз. Можно имитировать такой вид цикла либо через прямой цикл while, либо даже через цикл for, но часто результат оказывается немного менее элегантным. Я признаю, что конкретные применения этого паттерна довольно редки, но они существуют. На ум приходит консольное приложение на основе меню:
do {
char c = read_input();
process_input(c);
} while (c != 'Q');
Он также доступен на C#, в котором нет макросов. Я не уверен, почему кто-то проголосовал против этого ответа, но я считаю его почти правильным, за исключением того, что он упустил из виду явный «0» в условии while. Пожалуйста, народ, если вы проголосовали против чей-то ответ, прокомментируйте.
Я не был тем, кто проголосовал против; однако вопрос очень конкретный, и в целом ответ верен, но вне контекста.
Утверждение верно, но не в контексте.
Это помогает сгруппировать несколько операторов в один, так что макрос, похожий на функцию, может фактически использоваться как функция. Предположим, у вас есть:
#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.
Очень четкий ответ. +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
....
Разве #define FOO(x) if (true){ foo(x); bar(x); } else void(0) не будет работать, даже если он намного уродливее?
Но как насчет #define FOO(x) foo(x), bar(x), чтобы использовать запятую вместо точки с запятой. Разве это не сработало бы в другом случае?
@ user10607: Да, это работает, если ваши расширения макросов представляют собой список выражения. Однако, если вы хотите включить if или while в расширение, этот трюк тоже не сработает.
@Adisak, я думаю, это сломает ваш пример: FOO(x) , bar();, потому что ,bar() будет поглощен void(0),bar(). т.е. оператор запятой будет применен не в том месте, что приведет к странному поведению. do { ... } while(0) очень хорошо зарекомендовал себя, делает то, что должен делать. Самое главное, когда опытные разработчики видят do { ... } while(0), написанный другими, они могут точно видеть, что происходит. Зачем использовать что-то еще, что могло бы беспокоить только опытных разработчиков, даже если это может быть правильным?
@AaronMcDaid Что ж, оператор запятой действительно лажает, как вы упомянули. Но это также плохо сказывается на деле. Это недопустимо для C++ 'do {} while (0), printf ("hello world \ n");' либо. Я указываю вам на этот тест: ideone.com/TdN4YG Итак, мой уродливый макрос работает так же хорошо, как и макрос do while.
Кроме того, я никогда не говорил, что мой уродливый макрос лучше, чем версия do-while. На самом деле, я предпочитаю делать время. Я просто опровергал утверждение в ответе, что do-while - ЕДИНСТВЕННЫЙ способ сделать это.
Ах! Согласен @Adisak. Это интересно. Я полагаю, что do {...} while(0) - это традиционный способ, и поэтому лучше быть последовательным и придерживаться его, если только альтернатива не делает то, чего не может do {...} while(0).
Я по-прежнему считаю, что это УЖЕСТВЕННОЕ и НЕКОТОРЫЕ кодирование, и его МОЖНО избежать, если оно написано правильно. это восходит к некоторым правилам C здравого смысла: ВСЕГДА использовать фигурные скобки в ваших операторах IF, даже если у вас есть только ОДНА операция после IF. Это должно быть стандартной практикой, ИМВХО ...
@LarryF: Проблема в том, что если вы пишете файл заголовка для использования другими, вы не можете выбирать, как другие люди будут использовать ваш макрос. Чтобы избежать неожиданных сюрпризов, нанесенных вашим пользователям, вы должны использовать метод, подобный описанному выше, чтобы заставить ваш макрос вести себя так, как любой другой оператор C.
Как замечательно, что этот ответ получил отрицательные голоса
Мне кажется, что использование или неиспользование точки с запятой не имеет значения?
@Max Вы используете свой макрос в операторе if без фигурных скобок, как в моем первом примере? В большинстве других ситуаций точка с запятой не имеет большого значения.
да. Я делаю, если (правда) {FOO (x); } и if (true) {FOO (x)}, точка с запятой и точка с запятой не имеют значения без do while (), но случай, когда используется do while (), не компилируется без точки с запятой
@Max Как и в моем примере, вам нужно, чтобы нет использовал фигурные скобки, и использовал предложение else.
Я использую предложение else, и даже без фигурных скобок я могу вызывать макрос с точкой с запятой или без нее.
@Max, хорошо, если у вас все еще есть вопросы о том, что с вами происходит, задайте новый вопрос вместо комментариев. Спасибо.
Любой, кто видит это сейчас, может найти это информативным: gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/… «Составной оператор, заключенный в круглые скобки, может отображаться как выражение в GNU C. Это позволяет вам использовать циклы, переключатели и локальные переменные в выражении». . Поэтому мы можем просто использовать #define FOO (x) ({foo (x); bar (x);})
@GregHewgill: извините, но я полностью согласен с LarryF: если вы настолько ленивы, что не ставите фигурные скобки, когда выражение только одно, ну, вы заслуживаете, что мой код не работает для вас. Не говоря уже о том, что не ставить фигурные скобки - это всегда - плохая идея, поскольку если вы добавите еще один оператор после if без фигурных скобок, возможно, вы подумаете, что он будет выполнен, только если вы введете if, но это не так.
Я не понимаю, почему брекеты не работают.
@AaronFranke, потому что {...}; фактически состоит из двух операторов: части {...} и еще одного пустого оператора после нее. делать {...} в то время как (0); это одно заявление, тем не менее. Ларри / Марко, когда пользователи пишут FOO (a, b, c), они ожидают (если явно не указано иное) результатом будет выражение, очень похожее на вызов функции, возвращающей void. Ваш подход на самом деле небрежный. Соглашение о стиле кода вашего пользователя не должно вас беспокоить.
Интересно отметить следующую ситуацию, когда цикл 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;
}
Это расширение GCC. Однако в C++ 11 вы можете сделать то же самое с лямбда-выражением.
Выражения оператора TIL. Аккуратный. Как это выглядит с лямбдой? Мне любопытно. Лямбда возвращает функтор, по сути, вам все равно нужно вызвать его, а затем вернуть результат из макроса. и лямбда, и вызов должны формировать единый оператор. Как ты это делаешь? оператор ,?
Взгляните на этот вопрос.