该程序的输出为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;
}
为什么没有错误?
答案 0 :(得分:7)
以下是汇编语句printf("The value of A is %d\n", A);
的步骤:
printf
,(
,"The value of A is %dn"
,,
,A
,)
和{{1} }。;
是一个宏,它扩展为2个令牌A
和-
。B
也是一个宏,并扩展为B
和-
。C
还是一个宏,并扩展为C
。5
)。在此示例中,令牌是相同的。0a
,printf
,(
,"The value of A is %d\n"
,,
,-
,-
,5
,)
将具有两个参数的函数调用与;
匹配:格式字符串和常量表达式printf
-
{ {1}},其在编译时的值为-
。代码因此等效于5
。它将产生输出:
5
此宏序列被扩展为标记,而不是严格地由字符序列扩展,因此printf("The value of A is %d\n", 5);
不会扩展为The value of A is 5
,而是扩展为A
。好的C编译器在将源预处理为文本输出时会插入一个额外的空间,以确保所生成的文本在重新解析时产生相同的标记序列。但是请注意,C标准并未提及对文本输出进行预处理的任何内容,它仅将预处理指定为解析阶段之一,并且对于编译器而言,在对文本输出进行预处理时不引入潜在的副作用是实现质量的问题。>
还有一个单独的功能,可以将令牌组合成预处理器中的新令牌,称为令牌粘贴。它需要特定的运算符--5
,并且使用起来非常棘手。
还请注意,宏应在每个参数周围用括号定义,并在整个扩展数周围用括号定义,以防止出现运算符优先级问题:
- -5
答案 1 :(得分:1)
两个连续的破折号没有组合到单个减量运算符--
中,因为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
定义了一个类似于对象的宏,该宏导致每个宏名称的后续实例都被构成该指令其余部分的预处理令牌的替换列表所替代。
因此,两个破折号-
构成了两个不同的标记,因此它们在预处理器的输出中保持彼此独立。
答案 2 :(得分:0)
每当在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 指令