Препроцессор C рассматривает идентификатор как объектный, а не как функциональный - PullRequest
2 голосов
/ 16 ноября 2011

Это очень упрощенная версия кода, с которым я только что столкнулся на работе:

#include <stdio.h>

#define F(G) G(1)
#define G(x) x+1

int main() {
  printf("%d\n", F(G));
}

печатает 2.

Теперь я вижу, что F (G) расширяется до G(1), а затем G (1) расширяется до 2, но мне не понятно почему.Я ожидал получить ошибку, что G не является функцией из строки printf.

Как препроцессор разбирает код следующим образом?

Ответы [ 4 ]

6 голосов
/ 16 ноября 2011

Функциональный макрос вызывается только в том случае, если за его именем следует (.

В F(G), G не сопровождается (, поэтому Gнет вызова макроса.

В F(G) G(1), G является параметром макроса и, следовательно, не заменяется макросом напрямую (это очень запутанный макрос, который у вас есть: -O).В G(1), G заменяется аргументом, соответствующим параметру G, который также бывает G.Эта замена затем повторно сканируется , а G(1) оценивается как 1 + 1.

Если мы переписываем ваши макросы, чтобы вы не использовали G несколькими различными способами, это оченьлегче понять:

#define F(x) x(1)
#define G(x) x + 1

Здесь F(G) заменяется на G(1).Затем он повторно сканируется и оценивается вызов G, что дает 1 + 1.

1 голос
/ 16 ноября 2011

Расширяя ответ Джеймса МакНеллиса, стандарт C99 предписывает:

6.10.3.4 Повторное сканирование и дальнейшая замена

1 После замены всех параметров в списке замены и #и ## обработка выполнена, все маркеры предварительной обработки меток удалены.Затем результирующая последовательность токенов предварительной обработки пересканируется вместе со всеми последующими токенами предварительной обработки исходного файла для замены других имен макросов.

2 Если имя заменяемого макроса найдено во время этого сканированиясписок замены (не включая остальную часть токенов предварительной обработки исходного файла), он не заменяется.Кроме того, если во вложенных заменах встречается имя заменяемого макроса, он не заменяется.Эти не замененные токены предварительной обработки имени макроса больше не доступны для дальнейшей замены, даже если они позднее (повторно) проверяются в контексте, в котором иначе токен предварительной обработки имени макроса был бы заменен.

3 Получившийся полностью замененный макросПоследовательность токенов предварительной обработки не обрабатывается как директива предварительной обработки, даже если она похожа на единицу, но все прагматические выражения унарных операторов внутри нее затем обрабатываются, как указано в 6.10.9 ниже.

0 голосов
/ 16 ноября 2011

Препроцессор делает один проход, но вы думаете, что он делает один проход на #define. Таким образом, во время прохождения препроцессор сопоставляет и заменяет F(G), но не соответствует ни одному G(x).

0 голосов
/ 16 ноября 2011

# определяет выполнение простой замены строки:

printf ("% d \ n", F (G));

переходит к

printf ("%d \ n ", G (1));

, что означает:

printf ("% d \ n ", 1 + 1);

...