Почему этот макрос заменен как 20 вместо 10?


1. #define NUM 10
2. #define FOO NUM
3. #undef NUM
4. #define NUM 20
5. 
6. FOO

когда я запускаю только препроцессор, выходной файл содержит 20.

однако, насколько я понимаю, препроцессор просто делает замены текста. Так что это то, что я думаю, что происходит (что, очевидно, неправильно, но idky):

  1. NUM определяется как 10.
  2. поэтому в строке 2 число заменяется на 10. Итак, теперь у нас есть "#define FOO 10".
  3. NUM не определено.
  4. NUM переопределен и теперь равен 20.
  5. FOO заменяется в соответствии со строкой 2, которая была до переопределения строки 4, и равна 10.

поэтому я думаю, что выход должен быть 10 вместо 20. Можете что-нибудь объяснить, где это пошло не так?

4 65

4 ответа:

в интересах сбора всех соответствующих спецификаций из стандартов я извлек эту информацию из потока комментариев и добавил номера разделов C++, основанные на проекте N4527 (нормативный текст идентичен в двух стандартах). Стандарт(ы) абсолютно ясны по этому вопросу.

  1. #define директивы препроцессора не подвергаются замене макросов.

    (C11 §6.10¶7; C++ §16[cpp] ¶6): предварительная обработка маркеры в директиве предварительной обработки не подлежат расширению макроса, если не указано иное.

  2. после замены макроса его заменяющим текстом выполняется повторное сканирование нового текста. Токены препроцессора в замене разворачиваются как макросы, если в этой точке программы имеется активное определение макроса для токена.

    (C11 §6.10.3¶9; C++ §16.3[cpp.заменить] ¶9) директива предварительной обработки формы

    # define identifier replacement-list new-line

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

  3. определение макроса активно из строки, следующей за #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 расширяется до содержит макросы, затем это расширяется, и так далее).

так что ваша последовательность шагов, на самом деле:

  1. NUM определяется как 10
  2. FOO определена как NUM
  3. NUM неопределенный и переопределенный как 20
  4. FOO увеличивается до NUM
  5. (сканирование) 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. Таким образом, последовательность шагов в этом коде:

  1. STR(X) определяется как #X
  2. STR_MACRO(X) определяется как STR(X)
  3. NUM определяется как 10
  4. STR_MACRO и NUM оба расширены; результат puts( STR(10) );
  5. (результат повторного сканирования последнего расширения)STR(10) увеличивается до "10"
  6. (результат повторного сканирования последнего расширение) дальнейшее расширение невозможно.