C многострочный макрос: do/while(0) против блока области видимости
Возможные дубликаты:
Какая польза от do while(0), когда мы определяем макрос?
Почему в макросах C/C++ иногда есть бессмысленные операторы do / while и if / else?
делать { … } while (0) для чего это нужно?
Я видел несколько многострочных макросов C, которые заключены в цикл do/while(0), например:
#define FOO \ делать { \ do_stuff_here \ do_more_stuff \ } while (0)
Каковы преимущества (если таковые имеются) написания кода таким способом по сравнению с использованием базового блока:
#define FOO \ {\ do_stuff_here \ do_more_stuff \ }
1 ответ
http://bytes.com/groups/c/219859-do-while-0-macro-substitutions
Андрей Тарасевич:
Вся идея использования версии do/while состоит в том, чтобы создать макрос, который будет расширяться в обычный оператор, а не в составной оператор. Это сделано для того, чтобы сделать использование макросов в стиле функции единообразным с использованием обычных функций во всех контекстах.
Рассмотрим следующий эскиз кода
if (<condition>)
foo(a);
else
bar(a);
где 'foo' и 'bar' являются обычными функциями. Теперь представьте, что вы хотите заменить функцию 'foo' макросом вышеуказанной природы.
if (<condition>)
CALL_FUNCS(a);
else
bar(a);
Теперь, если ваш макрос определен в соответствии со вторым подходом (просто '{' и '}'), код больше не будет компилироваться, потому что 'истинная' ветвь 'if' теперь представлена составным оператором. И когда вы ставите ';' после этого составного оператора вы завершили весь оператор 'if', потеряв таким образом ветку 'else' (отсюда и ошибка компиляции).
Один из способов исправить эту проблему - помнить, что нельзя ставить ';' после макроса "вызовы"
if (<condition>)
CALL_FUNCS(a)
else
bar(a);
Это скомпилирует и сработает, как и ожидалось, но это не единообразно. Более элегантное решение - сделать так, чтобы макрос расширялся в обычный оператор, а не в составной. Один из способов добиться этого - определить макрос следующим образом
#define CALL_FUNCS(x) \
do { \
func1(x); \
func2(x); \
func3(x); \
} while (0)
Теперь этот код
if (<condition>)
CALL_FUNCS(a);
else
bar(a);
скомпилирует без проблем.
Тем не менее, обратите внимание на небольшое, но важное различие между моим определением CALL_FUNCS
и первая версия в вашем сообщении. Я не ставил;
после } while (0)
, Положить ;
в конце этого определения сразу же исчезнет весь смысл использования do/while и сделает этот макрос в значительной степени эквивалентным версии составного оператора.
Я не знаю, почему автор кода, который вы цитировали в своем исходном сообщении, поставил этот ;
после while (0)
, В этой форме оба варианта эквивалентны. Вся идея использования версии do/while состоит не в том, чтобы включить этот финал ;
в макрос (по причинам, которые я объяснил выше).