(Странно?) Поведение препроцессора GCC - PullRequest
13 голосов
/ 28 января 2011

Я думаю, большинство из вас, кто работал с C / C ++, интуитивно понимают, как работает препроцессор (более или менее).До сегодняшнего дня я так думал, но моя интуиция оказалась неверной.Вот история:

Сегодня я что-то пробовал, но не могу объяснить результат.Сначала рассмотрим следующий код:

#define A B
#define B A

A
B

Что происходит?Что ж, результат после его компиляции с флагом -E таков:

A
B

Ну, ладно, может быть, не то, что кто-то ожидал, но это объяснимо.Я предполагаю, что препроцессор как-то понял, что есть какая-то проблема, и ничего не сделал.

Следующее, что я попробовал, было следующее:

#define A B
#define B A C
#define C x

A
B

Теперь к необъяснимому для меня результату:

A x
B x

Как это случилось?Я не могу придумать разумного способа, как это произошло.Первая команда (#define AB) не может быть выполнена, потому что тогда A будет заменено на B, и конечный результат должен быть одинаковым для обоих.Но если это не так, то «A x» не может произойти!

Мой вопрос: что мне не хватает?Очевидно, я не знаю точный способ работы препроцессора.Вы знаете какие-либо источники об этом?

Ответы [ 4 ]

13 голосов
/ 28 января 2011

Макрос самообращения поясняет. Расширение применяется глубоко, но останавливается, когда макрос ссылается на себя.

5 голосов
/ 28 января 2011

Каждая цепочка замен может посещать любое макроопределение не более одного раза.Среди прочего, это означает, что вы не можете иметь рекурсивные макросы.

Подстановки для вашего второго примера будут выглядеть так:

A --[evaluate A]--> B --[evaluate B]--> A C --[evaluate C]--> A x
B --[evaluate B]--> A C --[evaluate A,C]--> B x

Во время последнего шага первой строки,A не оценивается, поскольку он уже был вызван ранее.Аналогично, во второй строке оценка останавливается на B, поскольку она уже была посещена на первом этапе.

Соответствующим разделом стандарта C99 будет 6.10.3.4 Повторное сканирование и дальнейшая замена .

5 голосов
/ 28 января 2011
#define A B
#define B A C
#define C x

A -> B -> A C -> A x 
B -> A C -> B x

Расширение является токеном с помощью токена "lazily"

3 голосов
/ 28 января 2011

Ну, хорошо, может быть, не то, что кто-то ожидал, но это объяснимо.Я предполагаю, что препроцессор как-то выяснил, что есть какая-то проблема, и ничего не сделал.

Нет.Если препроцессор выполняет расширение, он расширяет символ только один раз.Итак, в вашем первом примере для A: A расширяется до B, B расширяется до A, и здесь расширение останавливается.Во второй строке B расширяется до A, который расширяется до B, где расширение останавливается, потому что мы уже расширили B.

Если вы примените логику ко второму примеру, тогда результат станет сразу очевидным.

...