3.10.3 Проглатывание точки с запятой
Зачастую желательно определить макрос, который раскрывается в составное оператор. Например, рассмотрите следующий макрос, перемещающий указатель (аргумент p указывает, где его найти) через пробельные символы:
#define SKIP_SPACES(p, limit) \
{ char *lim = (limit); \
while (p < lim) { \
if (*p++ != ' ') { \
p--; break; }}} Здесь обратный слеш-новая строка используется для разделения определения макроса, которое должно быть в одной логической строке, чтобы оно напоминало способ форматирования такого кода, если бы он не был частью определения макроса.
Вызов этого макроса может выглядеть так SKIP_SPACES (p, lim). Строго говоря, вызов раскрывается в составной оператор, который представляет собой полный оператор без необходимости точки с запятой в конце. Однако, поскольку он выглядит как вызов функции, для минимизации путаницы его можно использовать как вызов функции, добавив точку с запятой в конце, как в SKIP_SPACES (p, lim);
Это может вызвать проблемы перед else операторами, потому что точка с запятой фактически является пустым оператором. Предположим, вы напишете
if (*p != 0) SKIP_SPACES (p, lim); else ...
Наличие двух операторов — составного оператора и пустого оператора — между if условием и else делает код на C некорректным.
Определение макроса SKIP_SPACES можно изменить, чтобы решить эту проблему, используя оператор do ... while. Вот как:
#define SKIP_SPACES(p, limit) \
do { char *lim = (limit); \
while (p < lim) { \
if (*p++ != ' ') { \
p--; break; }}} \
while (0) Теперь SKIP_SPACES (p, lim); раскрывается в
do {...} while (0); что является одним оператором. Цикл выполняется ровно один раз; большинство компиляторов не генерируют дополнительный код для этого.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-4.9.4/cpp/Swallowing-the-Semicolon.html