Почему этот макрос заменен как 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определяется как10FOOопределена какNUMNUMнеопределенный и переопределенный как20FOOувеличивается до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)определяется как#XSTR_MACRO(X)определяется какSTR(X)NUMопределяется как10STR_MACROиNUMоба расширены; результатputs( STR(10) );- (результат повторного сканирования последнего расширения)
STR(10)увеличивается до"10"- (результат повторного сканирования последнего расширение) дальнейшее расширение невозможно.