一个极其隐蔽而又坑爹的bug

像标题说的那样,bug很隐蔽,所以花了不少时间debug。

先上代码:

1
2
3
4
5
6
#define MAX(a, b) ((a) > (b) ? (a) : (b))

float max;
for(int i = 0; i < n; i++) {
max = MAX(max, data[data_index++]);
}

代码很简单,为了体现问题本质,我做了简化。

这段代码的目的就是从data数组中的某段连续n个元素中找出最大值,索引data_index之前被设定在data数组中的某一位置。

data_index++会把data_index加一,然后返回加一之前的值,这看起来很正确,可以一个挨一个地遍历数组中的某段连续n个元素。

其实不然,MAX宏展开后是这样的max > data[data_index++] ? max : data[data_index++](去掉了括号),如果max大于data[data_index++]并取max则没问题,但是如果max不大于data[data_index++],那么data_index++就做了两次。问题就出在这里。

也许把他们写在一起可以很容易看出问题,但是宏定义一般写在开头,而找最大值的这段代码在程序中部,两者相差很远。我当时费了半天劲才找到这个bug,很难想到问题出是在MAX宏上,其实还不如说问题出在data_index++上,这种自增操作要用就最好单独拿出来,或者保证不会被用于宏拓展,或者像《Google C++编程规范》中说的那样,尽量用内联函数来替代宏。

记下这个bug,起个提醒作用。