2011-01-27 2 views
12

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

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

#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» могло случиться!

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

+0

И вот почему #defines нужно избегать ... – Goz

+0

Да, это ДРУГОЙ разум. Не то, чтобы я имел в виду не использовать их вообще. Для некоторых задач они очень полезны (и способ пойти i.m.o.). – George

ответ

13

Self-Referential Macros поясняет. Расширение применяется глубоко, но останавливается, как только макрос ссылается на себя.

+0

Хорошая и полезная ссылка, спасибо! – George

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

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

Расширение является лексемой по лексеме «ленивый»

3

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

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

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

5

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

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

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

+0

Спасибо за объяснение! Я думал в контексте грамматик и языков, и я полностью пропустил это очевидное объяснение. – George

Смежные вопросы