首页 > 解决方案 > #if 指令中的评估顺序:宏扩展与“定义”关键字

问题描述

当 c 预处理器运行#if/#elif预处理指令时,它对直接跟随的标记执行 4 次操作:

  1. defined {identifier}1if{identifier}定义替换每一个出现,0否则。
  2. 调用所有宏。
  3. 用 替换所有剩余的标识符0
  4. 将结果解析并评估为constant-expression.

现在,从标准 (c99, 6.10.1) 中可以清楚地看出,步骤 3 和 4 实际上是按此顺序发生的,并且在完成 1 和 2 之后。但我找不到关于 1 和 2 顺序的任何说明。

从我所做的一些有限测试来看,gcc 似乎根据令牌的顺序执行步骤 1 和 2 - in defined MACROdefined首先执行,但在MACRO(defined ID)宏中执行。

这种行为是标准要求的吗?实现定义?不明确的?

标签: clanguage-lawyerc-preprocessorstandardsc99

解决方案


首先执行您的第 2 步。顺序为步骤 2、1、3、4:

C 2018 6.10.1 4 说[强调我的]:

在评估之前,预处理标记列表中将成为控制常量表达式的宏调用被替换(除了那些被defined一元运算符修改的宏名称),就像在普通文本中一样......</p>

6.10.1 1 表示#ifor的控制表达式#elif应该是一个整数常量表达式,它可以另外包含表达式or :defined identifierdefined ( identifier )

...如果标识符当前被定义为宏名称(即,如果它是预定义的,或者如果它是预处理指令的主题,而没有具有相同主题标识符的中间指令),则评估为 1,否则为0。#define#undef

因此,按照 6.10.1 4 执行第一次宏替换,然后执行表达式的评估。

请注意,尽管宏替换首先发生,但如果它生成defined令牌,它们不一定会被评估,因为 6.10.1 4 继续说:

…如果令牌defined是作为这个替换过程的结果生成的…,行为是未定义的。

然后按照 6.10.1 4 再次执行您的第 3 步:

... 在所有因宏扩展和defined一元运算符而导致的替换完成后,所有剩余的标识符(包括那些在词法上与关键字相同的标识符)都被替换为 pp-number 0,然后将每个预处理标记转换为一个标记…</p>

然后评估控制表达式,您的步骤 4:

... 生成的标记组成控制常量表达式,根据 6.6 的规则进行评估...</p>


推荐阅读