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 состоит не в том, чтобы включить этот финал ; в макрос (по причинам, которые я объяснил выше).

Другие вопросы по тегам