#define 在#define 中;在预处理器中会发生什么?

use*_*069 1 c-preprocessor preprocessor-directive

如果我有:

#define X 5
#define Y X
Run Code Online (Sandbox Code Playgroud)

这种事情的预处理器会发生什么?它是否遍历整个文件并将每个 X 更改为 5,然后返回到下一个定义,然后将每个 Y 更改为 5(因为在前一次迭代中 Y 得到了 5)?

tor*_*rek 5

C 标准对于宏的扩展方式有一个特殊的术语。

实际上,宏名称存储在“此时定义的所有宏”的大表中。左侧的每个表条目“宏名称”(以及中间的任何参数)和右侧的“扩展令牌流”。

当要扩展宏时(因为它出现在某个非预处理器行中,或位于必须扩展的预处理器行中的某个位置——例如,您可以#define STDIO <stdio.h>然后#include STDIO),表条目被“涂成蓝色” ,然后读取替换令牌流(使用标准也规定的参数扩展)。

如果替换令牌流包含原始宏名称,则不再匹配,因为“蓝色油漆”覆盖了名称。

当替换令牌流被完全处理后,“蓝色油漆”被移除,重新暴露名称。

因此:

#define X 5
Run Code Online (Sandbox Code Playgroud)

X:(无参数)添加5到表中。

然后:

#define Y X
Run Code Online (Sandbox Code Playgroud)

添加:Y:(无参数),X到表中。

在文件后面的某个地方,您可能会出现 token Y。假设以上都没有被#undef编辑(从表中删除),编译器必须首先“将表条目绘制为Y蓝色”并将标记替换Y为标记X

接下来,编译器必须“将表条目绘制为X蓝色”,并用 token 替换Xtoken 5

令牌5不是预处理器宏名称(根据定义它不能是),因此令牌5超出了预处理阶段的范围。现在,“蓝色油漆”从X表条目中删除,就像这样;然后从Y条目中删除“蓝色油漆” ,这也完成了。


如果你改写:

#define Y Y, Y, Y, the letter is called Y!
Run Code Online (Sandbox Code Playgroud)

那么在遇到后面的 token 时Y,序列将是:

  1. Y蓝色的油漆条目
  2. 删除替换令牌序列: Y , Y , Y , the letter is called Y !
  3. 检查表中的每个替换标记——因为Y那些不匹配和,不能匹配的标记被涂成蓝色,所以这些都被传递给编译器的其余部分;the, letter, is, 和called必须检查但可能不在表中,因此传递; Y仍然涂成蓝色所以不匹配并传递,!不能匹配并传递。
  4. 去除蓝色油漆,恢复扩张