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 делает код некорректным.
Определение макроса 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-11.4.0/cpp/Swallowing-the-Semicolon.html