C 预处理器中的宏观危害

macro hazards in c preprocessor

本文关键字:宏观 预处理 处理器      更新时间:2023-10-16

>我定义了以下宏,

#define abss(a) a >= 0 ? a : -a

在调用它时,

int b=-1;
int c = abss(b);
printf("%dn",c);

它应该以 b>= 0 ? b : --b 的形式替换,它应该输出 -2,但它在我的 Bloodshed/DevC++ 编译器 1 中输出。

正在分析C语言以进行检查,因此我必须知道上述C语言的情况实际上发生了什么。输出结果 1 是针对我正在使用的 CPP 编译器还是什么???

你的代码

int b=-1;
int c = abss(b);
printf("%dn",c);

由预处理器转换为:

int b=-1;
int c = b >= 0 ? b : -b;
printf("%dn",c);

int b=-1在 C 域中,而abss(b)在编译器到达int b=-1之前由预处理器扩展)

为什么你认为它应该输出 -2?

#define abss(a) a >= 0 ? a : -a

所以这个: int c = abss(b)

成为 int c = b>= 0 ?b : -b

b 为 -1,因此计算结果为 1

顺便说一下,你应该把宏参数的每次使用都括起来,因为你可能会被传递诸如 x + 1 之类的东西。 这会很奇怪。

更不用说abss(++x)的结果了

您的宏将扩展到

b >= 0 ? b : -b;

不要b >= 0 ? b : --(-b);或你所期望的任何东西。

你是在暗示

int x = -4;
-x; 

应该减少x吗?

底线 - 尽可能避免使用宏。

宏处理器永远不会将两个相邻的标记附加在一起,即使它们之间没有空格,所以事实上,正如其他海报所提到的,您的宏扩展到- b1.

如果您希望两个令牌在预处理器扩展时成为一个,则必须使用令牌粘贴运算符##,类似于

#define abss(a) a >= 0 ? a : - ## a

这确实会做你想要的,从某种意义上说,负号和变量将形成一个标记。不幸的是(或者幸运的是,取决于你如何看待它!)粘贴的标记"-b"将是无效的,并且扩展将无法编译。

#define neg(a) -a
int b = -1;
neg(b);

在上述情况下,宏由预处理器扩展为 -b,这将导致值为 1。预处理器甚至不会意识到 b 的内容符号是负数,那么第二个-应该从哪里来形成前缀递减运算符呢?

即使你这样写:

 neg( -b );

宏替换是在令牌级别完成的,而不是作为文本搜索和替换。你会得到相当于- ( -b ),但不是--b

编辑:您在其他评论中链接到的手册已过时(甚至没有解决C99标准),并且非常糟糕。只是浏览一下,我发现了六个陈述,如果你认为它们是正确的,它们会让你看起来很愚蠢。不要使用它,除了点火。

此宏

#define abss(a) a >= 0 ? a : -a

确实很危险,但危险不在你认为的地方。帖子中的表达方式

int c = abss(b);

工作得很好。但是,请考虑当表达式不是简单变量,而是难以计算的函数时,或者当它是具有副作用的表达式时会发生什么。例如

int c = abss(long_calc(arg1, arg2));

int c = abss(ask_user("Enter a value"));

int c = abss(b++);

如果abss是一个函数,则所有三个调用都将在可预测的时间内产生良好的结果。但是,宏使第一次调用调用long_calcask_user两次(如果用户在再次提示时输入不同的号码怎么办?),并且它会后递增两次b

此外,请考虑这个看似简单的表达式:

int c = abss(b) + 123;

由于+的优先级高于? :,因此生成的扩展将如下所示:

int c = b >= 0 ? b : -b + 123;

b为正时,不会添加123,因此表达式的含义将发生巨大变化!

最后一个缺点可以通过将表达式括在括号中来解决。您还应该将每个宏参数括在括号中,如下所示:

 #define abss(a) ((a) >= 0 ? (a) : -(a))

你得到行为的原因是预处理器只会用 b 替换 a,所以你的代码在预处理后会很有效

b >= 0 ? b : -b

而不是

b >= 0 ? b : --b

因此,当 b = -1 时,则它实际上将被视为 -(-1),因此变为 1。

您可以检查预处理器输出并亲自查看。