Подтвердить что ты не робот

(Странно?) Поведение препроцессора GCC

Я думаю, что большинство из вас, кто работал с 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 A B) не может быть выполнена, потому что тогда A будет заменено на B, и конечный результат должен быть таким же для обоих. Но если это не так, то нет никакого способа, чтобы "A x" могло случиться!

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

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

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 Повторное сканирование и дальнейшая замена.

Ответ 4

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

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

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