首页 > 解决方案 > 为什么这些连续的宏替换不会导致错误?

问题描述

该程序给出的输出为 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;
} 

为什么没有错误?

标签: cmacros

解决方案


以下是语句的编译步骤printf("The value of A is %d\n", A);

  • 词法解析器生成预处理标记printf("The value of A is %dn",A和。);
  • A是一个扩展为 2 个标记-B.
  • B也是一个宏,并被扩展为-and C
  • C又是一个宏并被扩展为5.
  • 然后将标记转换为 C 标记,从而为未转换为正确 C 标记的预处理标记产生错误(例如:)0a。在此示例中,令牌是相同的。
  • 编译器根据 C 语法解析生成的序列:printf, (, "The value of A is %d\n", ,, -, -, 5, ),;匹配printf带有 2 个参数的函数调用:格式字符串和常量表达式- - 55在编译时计算为 。
  • 因此,该代码等效于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

推荐阅读