Почему этот макрос заменен как 20 вместо 10?
1. #define NUM 10
2. #define FOO NUM
3. #undef NUM
4. #define NUM 20
5.
6. FOO
когда я запускаю только препроцессор, выходной файл содержит 20.
однако, насколько я понимаю, препроцессор просто делает замены текста. Так что это то, что я думаю, что происходит (что, очевидно, неправильно, но idky):
- NUM определяется как 10.
- поэтому в строке 2 число заменяется на 10. Итак, теперь у нас есть "#define FOO 10".
- NUM не определено.
- NUM переопределен и теперь равен 20.
- FOO заменяется в соответствии со строкой 2, которая была до переопределения строки 4, и равна 10.
поэтому я думаю, что выход должен быть 10 вместо 20. Можете что-нибудь объяснить, где это пошло не так?
4 ответа:
в интересах сбора всех соответствующих спецификаций из стандартов я извлек эту информацию из потока комментариев и добавил номера разделов C++, основанные на проекте N4527 (нормативный текст идентичен в двух стандартах). Стандарт(ы) абсолютно ясны по этому вопросу.
#define
директивы препроцессора не подвергаются замене макросов.(C11 §6.10¶7; C++ §16[cpp] ¶6): предварительная обработка маркеры в директиве предварительной обработки не подлежат расширению макроса, если не указано иное.
после замены макроса его заменяющим текстом выполняется повторное сканирование нового текста. Токены препроцессора в замене разворачиваются как макросы, если в этой точке программы имеется активное определение макроса для токена.
(C11 §6.10.3¶9; C++ §16.3[cpp.заменить] ¶9) директива предварительной обработки формы
# define identifier replacement-list new-line
определяет объект-как макрос это приводит к тому, что каждый последующий экземпляр имени макроса заменяется списком замены маркеров предварительной обработки, которые составляют остальную часть директивы. Затем список замены повторно сканируется для получения дополнительных имен макросов, как указано ниже.
определение макроса активно из строки, следующей за
#define
до#undef
для имени макроса или конца из досье.(C11 §6.10.3.5¶1; C++ §16.3.5[cpp.scope] ¶1) Определение макроса длится (независимо от структуры блока) до соответствующего
#undef
директива встречается или (если она не встречается) до конца блока предварительной обработки перевода. Макроопределения не имеют значения после фазы перевода 4.если мы посмотрим на программу:
#define NUM 10 #define FOO NUM #undef NUM #define NUM 20 FOO
мы видим, что определение макрос
NUM
в строке 1 длится ровно до линии 3. В этих строках нет заменяемого текста, поэтому определение никогда не используется; следовательно, программа фактически такая же, как:#define FOO NUM #define NUM 20 FOO
в этой программе, в третьей строке, есть активное определение для
FOO
С список заменыNUM
иNUM
С список замены20
. ЭлементFOO
заменяется на его список замены, что делает егоNUM
, а затем вновь проверяется на наличие макросов, в результате чегоNUM
заменяется его список замены 20. Эта замена снова пересканирована, но нет определенных макросов, поэтому конечным результатом является то, что токен20
остается для обработки в фазе перевода 5.
Замена текста выполняется там, где используется макрос, а не там, где вы написали
#define
. В точке вы используетеFOO
заменяетFOO
СNUM
иNUM
В настоящее время определяется как20
.
In:
FOO
препроцессор заменит его с
NUM
, то он заменитNUM
С тем, что он в настоящее время определяется как, что20
.эти начальные четыре строки эквивалентны:
#define FOO NUM #define NUM 20
стандарт C11 говорит (и другие версии C, и C++, говорят аналогично):
директива предварительной обработки формы
# define identifier replacement-list new-line
определяет объектно-подобный макрос, который заставляет каждый последующий экземпляр имени макроса заменяться списком замены маркеров предварительной обработки, которые составляют остальную часть директивы. Затем список замены повторно сканируется для получения дополнительных имен макросов, как указано ниже.однако он также говорит в другом часть (Спасибо Ричи за указание на это).
маркеры предварительной обработки в директиве предварительной обработки не подлежат расширению макроса, если не указано иное.
Итак, последующий экземпляр имени макроса который находится внутри другого
#define
директива на самом деле не заменить.ваши строки
#define FOO NUM
определяет, что, когда маркерFOO
позже найден (вне другого#define
директива!), он будет заменен на знакNUM
.после того, как маркер будет заменен, сканирование происходит, и если
NUM
сам по себе макрос, тоNUM
заменяется в этот момент. (И если чтоNUM
расширяется до содержит макросы, затем это расширяется, и так далее).так что ваша последовательность шагов, на самом деле:
NUM
определяется как10
FOO
определена какNUM
NUM
неопределенный и переопределенный как20
FOO
увеличивается доNUM
- (сканирование)
NUM
увеличивается до20
это поведение можно увидеть в другом распространенном трюке препроцессора, чтобы превратить определенное значение макроса в строку:
#define STR(X) #X #define STR_MACRO(X) STR(X) #define NUM 10 puts( STR_MACRO(NUM) ); // output: 10
если бы мы написали
puts( STR(NUM) )
тогда выход будетNUM
.выход
10
возможно потому, что, как раньше, второй#define
здесь фактически не расширяетсяSTR
. Таким образом, последовательность шагов в этом коде:
STR(X)
определяется как#X
STR_MACRO(X)
определяется какSTR(X)
NUM
определяется как10
STR_MACRO
иNUM
оба расширены; результатputs( STR(10) );
- (результат повторного сканирования последнего расширения)
STR(10)
увеличивается до"10"
- (результат повторного сканирования последнего расширение) дальнейшее расширение невозможно.