C++中错用宏定义造成的二义性

Ambiguity caused by incorrect use of macro definitions in C++

在写代码的时候经常会用到宏(#define)命令,最近我遇到了这两个错用宏的地方,写出来分析一下。

忘记了宏的参数只是进行简单的替换

代码如下

1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
//其实宏这么写是不对的,后面说
#define MAX(a,b) a>=b?a:b
int main(void){
int a,b;
cin>>a>>b;
cout<<MAX(a,b)<<endl;
return 0;
}

上面的代码实现的是一个简单的判断两数中大值的程序,现在这里没有问题。
但是,当MAX的参数带有副作用的时候就会出现问题了:

1
cout<<MAX(a--,b)<<endl;

当MAX的参数为MAX(a–,b)时,如果读入的数据为a>b,那么就出现错误了:

运行之后,虽然得出MAX值为11,但是,执行完毕cout<<MAX(a--,b)<<endl;之后,a的值就变成了10

因为define只是简单的宏替换,所以,当MAX的参数具有副作用的时候就会变成这样:

1
2
3
4
5
6
7
#define MAX(a,b) a>=b?a:b
//假如参数a具有副作用,那么cout<<MAX(a--,b)<<endl;就等价为:
if(a-->b){
cout<<a--<<endl
}else{
cout<<b<<endl;
}

意味着如果读入的数据是a>b,那么a--将执行两次。

将MAX宏改写成等价的if语句后,运行结果如下:

与执行MAX宏的结果一样。

宏没有加括号造成的二义性

前面代码里写到#define MAX(a,b) a>=b?a:b这么写是不对的,原因如下。
在这里如果调用的语句中还有其他的表达式,也会造成二义性!考虑下面的代码:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
#define MAX(a,b) a>b?a:b
int main(void){
int a,b;
cin>>a>>b;
int max=MAX(a,b)*2;
cout<<max<<endl;
return 0;
}

此处int max=MAX(a,b)*2;我们预期的是将a和b中的大值乘以2再赋值给变量max
但是正如前面所说的宏只是简单的替换,所以以上代码的实际含义是:

1
2
3
#define MAX(a,b) a>=b?a:b
//那么int max=MAX(a,b)*2;就等价为:
int max=a>=b?a:b*2;

运行结果如下:

当a>=b时,*2不会被执行,因为编译器把b*2当做了三目运算符的一个表达式。

所以,宏命令最安全也是最正确的写法是:

  1. 宏命令一定要加括号,保证宏命令作为独立的表达式。
  2. 宏命令的参数也一定要加括号,保证每个参数的正确使用(保证副作用不会影响其他的参数)。
1
#define MAX(a,b) ((a)>=(b)?(a):(b))

注意:千万要记得宏只是简单粗暴的替换,有可能会产生二义性!

全文完,若有不足之处请评论指正。

微信扫描二维码,关注我的公众号。

本文标题:C++中错用宏定义造成的二义性
文章作者:查利鹏
发布时间:2016/03/14 19:07
本文字数:686 字
原始链接:https://imzlp.com/posts/38656/
许可协议: CC BY-NC-SA 4.0
文章禁止全文转载,摘要转发请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!