Macros 为什么这个宏被替换为20而不是10?

Macros 为什么这个宏被替换为20而不是10?,macros,c-preprocessor,Macros,C Preprocessor,当我只运行预处理器时,输出文件包含20个 然而,据我所知,预处理器只是进行文本替换。这就是我认为正在发生的事情(这显然是错误的,但idky): NUM被定义为10 因此,在第2行中,NUM替换为10。现在我们有了“定义FOO 10” NUM未定义 NUM被重新定义,现在是20 FOO根据第2行进行更换,第2行在第4行重新定义之前,第10行 所以我认为输出应该是10而不是20。有什么能解释它哪里出错了吗?文本替换是在使用宏的地方进行的,而不是在编写\define的地方。在您使用FOO时,它将FOO

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

然而,据我所知,预处理器只是进行文本替换。这就是我认为正在发生的事情(这显然是错误的,但idky):

  • NUM被定义为10
  • 因此,在第2行中,NUM替换为10。现在我们有了“定义FOO 10”
  • NUM未定义
  • NUM被重新定义,现在是20
  • FOO根据第2行进行更换,第2行在第4行重新定义之前,第10行

  • 所以我认为输出应该是10而不是20。有什么能解释它哪里出错了吗?

    文本替换是在使用宏的地方进行的,而不是在编写
    \define
    的地方。在您使用
    FOO
    时,它将
    FOO
    替换为
    NUM
    NUM
    当前定义为
    20

    1. #define NUM 10
    2. #define FOO NUM
    3. #undef NUM
    4. #define NUM 20
    5. 
    6. FOO
    
    预处理器将用
    NUM
    替换它,然后将
    NUM
    替换为它当前的定义,即
    20

    最初的四行相当于:

    FOO
    
    <> P11 > 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 FOO NUM 
    #define NUM 20
    
    如果我们编写了
    put(STR(NUM))
    ,那么输出将是
    NUM

    10
    的输出是可能的,因为与以前一样,这里的第二个
    #define
    实际上没有向外扩展
    STR
    。因此,该代码中的步骤顺序是:

  • STR(X)
    定义为
    #X
  • STR\u宏(X)
    定义为
    STR(X)
  • NUM
    定义为
    10
  • stru宏
    NUM
    都已展开;结果为
    put(STR(10))
  • (重新扫描上次扩展的结果)
    STR(10)
    被扩展为
    “10”
  • (重新扫描上次扩展的结果)无法进一步扩展

  • 为了从标准中收集所有相关的规范,我从注释线程中提取了这些信息,并根据草案N45 27添加了C++区号,两个标准中的规范文本是相同的。这些标准在这个问题上是绝对明确的

  • #define
    预处理器指令不进行宏替换

    (C11×6.10±7;C++16〔CPP〕6〕:预处理指令中的预处理标记不受宏扩展,除非另有说明。< /P>

  • 用替换文本替换宏后,将重新扫描新文本。如果在程序中的该点上有标记的活动宏定义,则替换中的预处理器标记将扩展为宏

    (C11×6 .10 3,9;C++16.3〔CPP〕替换〕9)表格

    的预处理指令
    #定义标识符替换列表新行

    定义一个类似对象的宏,该宏使宏名称的每个后续实例被构成指令其余部分的预处理标记的替换列表替换。然后重新扫描替换列表以获得更多宏名称,如下所示

  • 宏定义从
    #define
    后面的行开始,一直处于活动状态,直到宏名称的
    #unde
    或文件结尾

    (C11<61.3.5,1;C++16.3.5[CPP.Simult])1,宏定义持续(独立于块结构),直到遇到相应的<代码>“unDEF指令”或(如果没有遇到)直到预处理翻译单元结束为止。在翻译阶段4之后,宏定义没有意义

  • 如果我们看一下该计划:

    #define STR(X) #X
    #define STR_MACRO(X) STR(X)
    #define NUM 10
    
    puts( STR_MACRO(NUM) );     // output: 10
    
    我们看到第1行中的
    NUM
    的宏定义一直持续到第3行。这些行中没有可替换的文本,因此从未使用该定义;因此,该计划实际上与以下内容相同:

    #define NUM 10
    #define FOO NUM
    #undef NUM
    #define NUM 20
    FOO 
    
    在这个程序的第三行,有一个活动的定义,用于
    FOO
    ,具有替换列表
    NUM
    ,以及
    NUM
    ,具有替换列表
    20
    FOO
    被替换为其替换列表,使其成为
    NUM
    ,然后再次扫描
    #define FOO NUM
    #define NUM 20
    FOO