为什么这个macros被replace为20而不是10?

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

当我只运行预处理器时,输出文件包含20个。

但是,据我所知,预处理器只是简单地进行文本replace。 所以这就是我认为正在发生的事情(这显然是错误的,但是爱迪生):

  1. NUM被定义为10。
  2. 因此,在第二行中,NUM被replace为10.因此,现在我们有“#F10 FOO 10”。
  3. NUM是未定义的。
  4. NUM被重新定义,现在是20。
  5. FOO被replace为第2行,在第4行的重新定义之前是10。

所以我认为输出应该是10而不是20。

为了收集标准中的所有相关规范,我从注释线索中提取了这些信息,并在草案N4527的基础上增加了C ++章节号(规范性文本在两个标准中是相同的)。 标准绝对清晰。

  1. #define预处理器指令不会进行macrosreplace。

    (C11§6.10¶7; C ++§16 [cpp]¶6):除非另有说明,否则预处理指令中的预处理标记不受macros扩展的限制。

  2. 在将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名称,如下所示。

  3. 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表NUMNUM有replace表20 。 将FOOreplace为其replace列表,使其为NUM ,然后再次对macros进行扫描,导致NUM被其replace列表20replace。该replace再次被重新扫描,但是没有定义的macros,因此结束结果是令牌20留在翻译阶段5进行处理。

文本replace是在使用macros的地方完成的,而不是你写的#define 。 在你使用FOO ,它用NUM代替FOONUM当前定义为20

在:

 FOO 

预处理器将用NUM取代它,然后它将用当前定义的NUMreplaceNUM ,即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,那么就会扩展,等等)。

所以你的一系列步骤实际上是:

  1. NUM定义为10
  2. FOO定义为NUM
  3. NUM未定义并重新定义为20
  4. FOO扩展到NUM
  5. (重新扫描) 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 。 所以这个代码中的步骤顺序是:

  1. STR(X)定义为#X
  2. STR_MACRO(X)定义为STR(X)
  3. NUM定义为10
  4. STR_MACRONUM都扩展了; 结果是puts( STR(10) );
  5. (上次扩展的重新扫描结果) STR(10)被扩展为"10"
  6. (上次扩展的重新扫描结果)不能进一步扩展。