为什么这个macros被replace为20而不是10?
1. #define NUM 10 2. #define FOO NUM 3. #undef NUM 4. #define NUM 20 5. 6. FOO
当我只运行预处理器时,输出文件包含20个。
但是,据我所知,预处理器只是简单地进行文本replace。 所以这就是我认为正在发生的事情(这显然是错误的,但是爱迪生):
- NUM被定义为10。
- 因此,在第二行中,NUM被replace为10.因此,现在我们有“#F10 FOO 10”。
- NUM是未定义的。
- NUM被重新定义,现在是20。
- FOO被replace为第2行,在第4行的重新定义之前是10。
所以我认为输出应该是10而不是20。
为了收集标准中的所有相关规范,我从注释线索中提取了这些信息,并在草案N4527的基础上增加了C ++章节号(规范性文本在两个标准中是相同的)。 标准绝对清晰。
-
#define
预处理器指令不会进行macrosreplace。(C11§6.10¶7; C ++§16 [cpp]¶6):除非另有说明,否则预处理指令中的预处理标记不受macros扩展的限制。
-
在将macrosreplace为replace文本后,重新扫描新文本。 replace中的预处理标记将作为macros展开,如果程序中该标记有一个活动的macros定义。
(C11§6.10.3¶9; C ++§16.3[cpp.replace]¶9)表单的预处理指令
# define identifier replacement-list new-line
定义了一个类似于对象的macros ,它使macros名的每个后续实例都被replace为构成指令其余部分的预处理标记的replace列表所取代。 然后重新扫描replace列表以获取更多macros名称,如下所示。
-
macros定义在
#define
的行中是有效的,直到macros名称的#undef
或文件的结尾。(C11§6.10.3.5¶1; C ++§16.3.5[cpp.scope]¶1)直到遇到相应的
#undef
指令或者(如果没有遇到任何指令),macros定义会持续(独立于块结构)直到结束预处理翻译单元。 macros观定义在翻译阶段4后没有意义。
如果我们看一下这个程序:
#define NUM 10 #define FOO NUM #undef NUM #define NUM 20 FOO
我们看到第1行中NUM
的macros定义完全持续到第3行。这些行中没有可replace的文本,因此定义从不使用。 因此,该scheme实际上是相同的:
#define FOO NUM #define NUM 20 FOO
在这个程序的第三行中,有一个有效的FOO
定义,带有replace表NUM
, NUM
有replace表20
。 将FOO
replace为其replace列表,使其为NUM
,然后再次对macros进行扫描,导致NUM
被其replace列表20replace。该replace再次被重新扫描,但是没有定义的macros,因此结束结果是令牌20
留在翻译阶段5进行处理。
文本replace是在使用macros的地方完成的,而不是你写的#define
。 在你使用FOO
,它用NUM
代替FOO
, NUM
当前定义为20
。
在:
FOO
预处理器将用NUM
取代它,然后它将用当前定义的NUM
replaceNUM
,即20
。
最初的四行相当于:
#define FOO NUM #define NUM 20
C11标准说(和其他版本的C和C ++一样):
格式为
# define identifier replacement-list new-line
的预处理指令定义了一个类似于对象的macros,它使macros名称的每个后续实例都被构成指令其余部分的预处理标记replace列表replace。 然后重新扫描replace列表以获取更多macros名称,如下所示。
然而它也说另一部分(感谢rici指出了这一点)。
除非另有说明,预处理指令中的预处理标记不受macros展开的限制。
因此,在另一个#define
指令中发现的macros名称的后续实例实际上不会被replace。
你的行#define FOO NUM
定义了当后来发现令牌FOO
时(在另一个#define
指令之外!),它将被令牌NUM
替代。
更换标记后, 重新扫描 ,如果NUM
本身就是一个macros,那么NUM
被replace。 (如果无论NUM
扩展到包含macros,那么就会扩展,等等)。
所以你的一系列步骤实际上是:
-
NUM
定义为10
-
FOO
定义为NUM
-
NUM
未定义并重新定义为20
-
FOO
扩展到NUM
- (重新扫描)
NUM
扩展到20
在另一个常见的预处理器技巧中可以看到这种行为,将macros定义的值转换为string:
#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"
- (上次扩展的重新扫描结果)不能进一步扩展。