【问题标题】:Why these consecutive macro replacements do not result in an error?为什么这些连续的宏替换不会导致错误?
【发布时间】:2018-06-24 22:34:52
【问题描述】:

这个程序给出的输出是 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;
} 

为什么没有错误?

【问题讨论】:

  • 那么--5的数值是多少?
  • 预处理器不组合标记,因此(实际上)相邻的- 符号作为预处理的结果在它们之间有空格- - 5(注意空格)相当于-(-(5)),这给出5的结果。

标签: c macros


【解决方案1】:

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

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

【讨论】:

  • 所以写一个新答案而不是链接到the duplicate? :-) 但是这个答案现在比旧的要好得多。
  • 是的,请以某种方式将两个问题和答案结合起来。
【解决方案2】:

两个连续的破折号不会组合成一个预减运算符--,因为 C 预处理器使用单独的标记,有效地在宏替换周围插入空格。通过gcc -E运行这个程序

#define A -B
#define B -C
#define C 5

int main() {
    return A;
}

产生以下输出:

int main() {
    return - -5;
}

注意第一个-后面的空格。

根据标准,宏替换是在预处理器标记级别执行的,而不是在单个字符级别 (6.10.3.9):

表单的预处理指令

# define identifier replacement-list new-line

定义了一个类似对象的宏,它使宏名称的每个后续实例都被构成指令其余部分的预处理标记的替换列表替换。

因此,两个破折号- 构成两个不同的标记,因此它们在预处理器的输出中保持相互分离。

【讨论】:

    【解决方案3】:

    每当我们在 C 程序中使用 #include 时,编译器都会将变量替换为其值。

    #define A -B
    #define B -C
    #define C 5
    

    所以当我们打印 A 时, 它将按以下步骤执行。

    A=>-B

    B=>-C

    A=>-(-C)=>C

    所以当我们打印 A 的值时,结果是 5。

    通常这些#define 语句用于声明要在整个代码中使用的常量的值。

    有关详细信息,请参阅 this link #define 指令

    【讨论】:

      猜你喜欢
      • 2012-05-14
      • 2013-05-20
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多