1. #define NUM 10
2. #define FOO NUM
3. #undef NUM
4. #define NUM 20
5.
6. FOO
当我只运行预处理器时,输出文件包含20.
但是,根据我的理解,预处理器只是替换文本。所以这就是我认为正在发生的事情(这显然是错误的但是很蠢):
所以我认为输出应该是10而不是20.可以解释哪里出错了吗?
答案 0 :(得分:64)
文本替换是在使用宏的地方完成的,而不是在你编写#define
的地方。在您使用FOO
时,它会将FOO
替换为NUM
,而NUM
目前定义为20
。
答案 1 :(得分:57)
为了从标准中收集所有相关规范,我从评论主题中提取了这些信息,并根据草案N4527添加了C ++部分编号(规范性文本在两个标准中是相同的)。标准在这个问题上是绝对清楚的。
#define
预处理程序指令不进行宏替换。
(C11§6.10¶7; C ++§16[cpp]¶6):除非另有说明,否则预处理指令中的预处理标记不受宏扩展的影响。
用替换文本替换宏后,将重新扫描新文本。如果在程序中该点处存在令牌的活动宏定义,则替换中的预处理器令牌将扩展为宏。
(C11§6.10.3¶9; C ++§16.3[cpp.replace]¶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
我们看到第1行中NUM
的宏定义完全持续到第3行。这些行中没有可替换的文本,因此从不使用该定义;因此,该计划实际上与:
#define FOO NUM
#define NUM 20
FOO
在此计划的第三行,有FOO
的有效定义,替换列表为NUM
,NUM
有替换列表20
。 FOO
替换为其替换列表,使其成为NUM
,然后再次扫描宏,导致NUM
被其替换列表20替换。重新扫描,但没有定义的宏,因此最终结果是令牌20
在转换阶段5留待处理。
答案 2 :(得分:20)
在:
FOO
预处理器会将其替换为NUM
,然后它将NUM
替换为当前定义的内容,即20
。
最初的四行相当于:
#define FOO NUM
#define NUM 20
答案 3 :(得分:14)
C11标准说(和其他版本的C和C ++一样):
形式为
# define identifier replacement-list new-line
的预处理指令定义了一个类似于对象的宏,它使宏名称的每个后续实例都被构成指令其余部分的预处理标记的替换列表替换。然后重新扫描替换列表以获取更多宏名称,如下所示。
然而,它也在另一部分说(感谢rici指出这一点)。
除非另有说明,否则预处理指令中的预处理标记不受宏扩展的影响。
因此,在另一个#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"