C预处理器的工作
下面的代码如何工作,换句话说C预处理器的算法是什么? 这适用于所有编译器吗?
#include #define ba #define a 170 int main() { printf("%i", b); return 0; }
预处理器只是将b
替换为程序中找到它a
任何位置,然后替换a
170
它只是纯文本替换。
适用于gcc。
它在§6.10.3(宏替换):
6.10.3.4重新扫描和进一步更换
1)在替换列表中的所有参数都已被替换并且#和##处理已经发生之后,将删除所有地标标记预处理标记。 然后,重新扫描生成的预处理标记序列以及源文件的所有后续预处理标记,以替换更多的宏名称。
更多段落陈述了一些补充规则和例外,但基本上就是这样。
虽然它可能违反了“单程”的某些定义,但它非常有用。 就像包含文件的递归预处理一样(§5.1.1.2p4)。
这个简单的替换(第一个b
用a
然后用170
)应该适用于任何编译器。 你应该小心处理更复杂的情况(通常涉及字符串化'#'
和令牌连接'##'
),因为角落情况至少由MSVC和gcc处理不同。
有疑问,您可以随时检查ISO标准( 草案可在线获取 ),看看事情应该如何运作:)。 第6.10.3节与您的案例最相关。
预处理器只是在符号出现时按顺序替换它们。 在这种情况下,定义的顺序无关紧要, b
由第一个替换,并且printf语句变为
printf("%i", a);
在被170
替换之后,它就变成了
printf("%i", 170);
如果定义的顺序改变了,即
#define a 170 #define ba
然后预处理器替换第一个,第二个定义变为
#define b 170
所以,最后printf语句变成了
printf("%i",170);
这适用于任何编译器 。
要获得详细信息,您可以尝试使用gcc -E
来分析您的预处理器输出,这可以轻松解决您的疑问
#define
只是为关键字赋值。
这里,’b’首先被赋值’a’,然后’a’被赋值’170’。 为简单起见,它可以表示如下:
b=a=170
这只是定义同一事物的另一种方式。
我想您正在尝试获取编译器如何处理源代码的信息。 要确切地知道你必须经历翻译阶段 。 每个编译器遵循的一般步骤(试图给出每个细节 – 从不同的博客和网站收集)如下:
-
第一步编译器 – 物理源文件字符映射到源字符集(如果需要,引入行尾指示符的换行符)。 Trigraph序列由相应的单字符内部表示替换。
-
编译器的第二步 – 删除新行字符和紧接在前的反斜杠字符的每个实例,拼接物理源行以形成逻辑源行。 非空的源文件应以换行符结尾,换行符前面不应有反斜杠字符。
-
编译器的第三步 – 源文件被分解为预处理标记和空白字符序列(包括注释)。 源文件不应以部分预处理标记或注释结束。 每个注释都被一个空格字符替换。 保留换行符。 是否保留或由一个空格字符替换其他空白字符的每个非空序列是实现定义的。
-
编译器的第四步 – 执行预处理指令并扩展宏调用。 #include预处理指令使得命名的头文件或源文件以递归方式从阶段1到阶段4进行处理。
-
Fivth Step by Compler – 字符常量和字符串文字中的每个转义序列都转换为执行字符集的成员。
-
编译器的第六步 – 相邻的字符串文字标记被连接,并且相邻的宽字符串文字标记被连接。
-
编译器的第七步 – 分隔标记的空白字符不再重要。 预处理令牌将转换为令牌。 由此产生的标记在语法和语义上进行分析和翻译。
-
最后一步 – 解决所有外部对象和函数引用。 链接库组件以满足对当前转换中未定义的函数和对象的外部引用。 所有这样的翻译器输出被收集到程序映像中,该程序映像包含在其执行环境中执行所需的信息。