C 预处理器中的宏观危害
macro hazards in c preprocessor
>我定义了以下宏,
#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
吗?
底线 - 尽可能避免使用宏。
宏处理器永远不会将两个相邻的标记附加在一起,即使它们之间没有空格,所以事实上,正如其他海报所提到的,您的宏扩展到- b
或1.
如果您希望两个令牌在预处理器扩展时成为一个,则必须使用令牌粘贴运算符##
,类似于
#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_calc
并ask_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。
您可以检查预处理器输出并亲自查看。
- 错误:无效的预处理指令 #i 的意思是 #if?
- C++预处理会生成变量成员、资源库和映射
- 使用预处理指令检查是否包含标头?
- 预处理的 C/C++ 文件是否特定于计算机?
- 使用 GCC 对 C 文件进行部分预处理(不删除 "define" 指令)
- 在 CPLEX 中求解线性规划,无需剪切和预处理
- CPP -D 选项,用于预处理 Fortran 代码
- 错误:粘贴"tmp_UINT"和"+"未提供有效的预处理令牌
- 任务计划程序库的预处理不起作用 - 多定义错误
- Eclipse 问题 - 编译期间不考虑 .c 和 .cpp 文件中定义的预处理
- 使用python预处理后,C++(opencv)中的垫子类型数据与image_to_array相同
- Howo 使用 cl 预处理为 masm 组装生成一个单独的文件
- 我有一个预处理的 C/C++ 源文件 (cacti.i).如何从这个 .i 文件生成可执行二进制文件,以便我可以像 ./
- 如何使用Visual Studio C/C++编译器(cl.exe)来预处理我的objective-C代码
- 是具有预处理前分支实现的结构违反ODR
- 与不完整的Cholesky预处理的共轭梯度返回特征库的意外错误
- Visual Studio C - 无法输出预处理文件
- 在海湾合作委员会中加快宏观预处理的任何方法
- Xmllint未设置,而在路径中找不到XMLLINT;跳过XML预处理
- 如何在预处理时读取文件