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-5.5.0/cpp/Swallowing-the-Semicolon.html