Как препроцессор C обрабатывает круговые зависимости?
Я хочу знать, как препроцессор C обрабатывает круговые зависимости (от #defines). Это моя программа:
#define ONE TWO
#define TWO THREE
#define THREE ONE
int main()
{
int ONE, TWO, THREE;
ONE = 1;
TWO = 2;
THREE = 3;
printf ("ONE, TWO, THREE = %d, %d, %d \n",ONE, TWO, THREE);
}
Здесь представлен вывод препроцессора. Я не могу понять, почему вывод такой. Я хотел бы знать различные шаги, которые препроцессор берет в этом случае, чтобы дать следующий вывод.
# 1 "check_macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "check_macro.c"
int main()
{
int ONE, TWO, THREE;
ONE = 1;
TWO = 2;
THREE = 3;
printf ("ONE, TWO, THREE = %d, %d, %d \n",ONE, TWO, THREE);
}
Я запускаю эту программу на linux 3.2.0-49-generic-pae и компилирую в gcc версии 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5).
Ответы
Ответ 1
Пока макрос препроцессора расширяется, это имя макроса не расширяется. Таким образом, все три символа определяются как сами:
ONE -> TWO -> THREE -> ONE (not expanded because expansion of ONE is in progress)
TWO -> THREE -> ONE -> TWO ( " TWO " )
THREE -> ONE -> TWO -> THREE ( " THREE " )
Это поведение задается в соответствии с разделом 6.10.3.4 стандарта C (номер секции из проекта C11, хотя, насколько я знаю, формулировка и нумерация раздела не изменяются со времени C89). Когда встречается имя макроса, оно заменяется его определением (и обрабатываются операторы препроцессора #
и ##
, а также параметры для макросов, подобных функциям). Затем результат повторно сканируется для большего количества макросов (в контексте остальной части файла):
2/Если имя заменяемого макроса найдено во время этого сканирования списка замещения (не считая остальных токенов предварительной обработки исходных файлов), он не заменяется. Кроме того, если какие-либо вложенные замены встречают имя заменяемого макроса, оно не заменяется & hellip;
Далее в статье говорится, что любой токен, который не заменен из-за рекурсивного вызова, фактически "заморожен": он никогда не будет заменен:
& hellip; Эти неперемещаемые маркеры предварительной обработки макросов больше не доступны для дальнейшей замены, даже если они были позже (повторно) рассмотрены в контекстах, в которых этот токен предварительной обработки имени макроса в противном случае был бы заменен.
Ситуация, на которую ссылается последнее предложение, редко возникает на практике, но вот простейший случай, о котором я мог подумать:
#define two one,two
#define a(x) b(x)
#define b(x,y) x,y
a(two)
Результат one, two
. two
расширяется до one,two
во время замены a
, а расширенный two
отмечен как полностью расширенный. Впоследствии b(one,two)
расширяется. Это уже не в контексте замены two
, но two
, который является вторым аргументом b
, был заморожен, поэтому он больше не расширяется.
Ответ 2
На ваш вопрос отвечает публикация ISO/IEC 9899: TC2 раздел 6.10.3.4 "Повторное сканирование и дальнейшая замена", пункт 2, который я цитирую здесь для вашего удобства; в будущем , пожалуйста, подумайте о том, чтобы прочитать спецификацию, когда у вас есть вопрос о спецификации.
Если имя заменяемого макроса найдено во время этого сканирования списка замены (не считая остальных токенов предварительной обработки исходных файлов), он не заменяется. Кроме того, если какие-либо вложенные замены встречают имя заменяемого макроса, он не заменяется. Эти неперемещаемые маркеры предварительной обработки макросов больше не доступны для дальнейшей замены, даже если они позже (повторно) рассматриваются в контекстах, в которых этот токен предварительной обработки имени макроса в противном случае был бы заменен.
Ответ 3
https://gcc.gnu.org/onlinedocs/cpp/Self-Referential-Macros.html#Self-Referential-Macros отвечает на вопрос о самореферентных макросах.
Суть ответа заключается в том, что, когда предварительный процессор обнаруживает самореляционные макросы, он вообще не расширяет их.
Я подозреваю, эта же логика используется для предотвращения расширения круговых макросов. В противном случае препроцессор будет в бесконечном расширении.
Ответ 4
В вашем примере вы выполняете макрообработку перед определением
переменные с тем же именем, поэтому независимо от результата
обработки макросов, вы всегда печатаете 1, 2, 3
!
Вот пример, где сначала определяются переменные:
#include <stdio.h>
int main()
{
int A = 1, B = 2, C = 3;
#define A B
#define B C
//#define C A
printf("%d\n", A);
printf("%d\n", B);
printf("%d\n", C);
}
Отпечатает 3 3 3
. Немного коварно, не комментируя #define C A
, изменяется поведение строки printf("%d\n", B);
Ответ 5
Здесь хорошая демонстрация поведения, описанного в ответах rici's и Эрика Липперта, то есть макроса имя не разворачивается, если оно встречается снова, уже расширяя тот же макрос.
Содержимое test.c
:
#define ONE 1, TWO
#define TWO 2, THREE
#define THREE 3, ONE
int foo[] = {
ONE,
TWO,
THREE
};
Вывод gcc -E test.c
(исключая начальные строки # 1 ...
):
int foo[] = {
1, 2, 3, ONE,
2, 3, 1, TWO,
3, 1, 2, THREE
};
(я бы опубликовал это как комментарий, но в том числе существенные блоки кода в комментариях выглядел неловко, поэтому вместо этого сделаю это ответом Wiki сообщества. Если вы считаете, что лучше включить его в качестве части существующий ответ, не стесняйтесь его скопировать и попросите удалить эту версию CW.)