c - 为什么这些连续的宏替换不会导致错误?
问题描述
该程序给出的输出为 5。但在替换所有宏后,它将导致--5
. 这应该会导致编译错误,试图减少5
. 但它编译并运行良好。
#include <stdio.h>
#define A -B
#define B -C
#define C 5
int main()
{
printf("The value of A is %d\n", A);
return 0;
}
为什么没有错误?
解决方案
以下是语句的编译步骤printf("The value of A is %d\n", A);
:
- 词法解析器生成预处理标记
printf
、(
、"The value of A is %dn"
、,
、A
和。)
;
A
是一个扩展为 2 个标记-
和B
.B
也是一个宏,并被扩展为-
andC
。C
又是一个宏并被扩展为5
.- 然后将标记转换为 C 标记,从而为未转换为正确 C 标记的预处理标记产生错误(例如:)
0a
。在此示例中,令牌是相同的。 - 编译器根据 C 语法解析生成的序列:
printf
,(
,"The value of A is %d\n"
,,
,-
,-
,5
,)
,;
匹配printf
带有 2 个参数的函数调用:格式字符串和常量表达式-
-
5
,5
在编译时计算为 。 因此,该代码等效于
printf("The value of A is %d\n", 5);
. 它将产生输出:The value of A is 5
这个宏序列被扩展为标记,而不是严格的字符序列,因此A
不扩展为--5
,而是扩展为- -5
。好的 C 编译器在将源代码预处理为文本输出时会插入一个额外的空格,以确保结果文本在重新解析时产生相同的标记序列。但是请注意,C 标准没有说明对文本输出的预处理,它仅将预处理指定为解析阶段之一,编译器在对文本输出进行预处理时不引入潜在的副作用是一个实现质量问题。
在预处理器中有一个单独的功能可以将标记组合成新标记,称为标记粘贴。它需要一个特定的运算符##
并且使用起来非常棘手。
另请注意,宏应在每个参数周围使用括号定义,在整个扩展周围使用括号定义,以防止运算符优先级问题:
#define A (-B)
#define B (-C)
#define C 5
推荐阅读
- c - Linux ext4 文件系统中如何使用套接字类型文件(S_IFSOCK)?
- java - 流口水比较集
- postgresql - 返回删除语句的每一行的 Postges 调用过程
- rtmp - 如何从 HLS 清单中获取 IMSC XML?
- python - 如何仅选择数据框中每个 userId 的最新日期,以及 userId 的列表?
- spring-boot - Spring Boot Quarts - 向rabbitmq发送失火
- excel - 如何使用满足两个条件的“查找”功能
- reactjs - 无法在 React 页面上的 img 标签中呈现 base 64
- spring-security - Spring Security - 记住我 cookie 无法在重启后存活
- javascript - 如果具有多个逻辑运算符的语句不起作用