未定义行为

来源:互联网 发布:2017双11实时数据 编辑:程序博客网 时间:2024/05/16 06:37

先看第一个小程序

int main(void)

{

       int i = 0;

       int a[] = {10, 20, 30};

       int b = 1*a[i++] + 2*a[i++] + 3*a[i++];

      

       printf(“b = %d\n”, b);

       return 0;

}

 

请思考:上述程序输出b = ?

 

思考完毕,再看第二个小程序

int main(void)

{

       int a = 5, b = 0;

       b = ++a * ++a;

      

       printf(“b = %d\n”, b);

       return 0;

}

 

请问,这里的b = ?

……

       用gcc 4.6.1编译后运行,我们发现第一个程序中输出的结果是:b = 60,而不是我们想的通过操作符的优先级得到的140。 同样,第二个程序输出的结果也是出乎意料的:b = 49。为什么会这样呢? o_O!!其实这是未定义行为。

 

        所谓的未定义(undefined)行为,即C++标准没有定义的行为。

 

        造成上述问题是由于对后缀自加或后缀自减的误解,因为没有保证自增或自减会在输出变量原值之后和对表达式的其它部分进行计算之前立即进行,也不能保证变量的更新会在表达式完成。

        其实,包含多个不确定的副作用的代码的行为总是被认为未定义,多个不确定副作用是指在同一个表达式中使用导致同一对象修改两次或修改以后又被引用的自增,自减和赋值操作符的任何组合。

 

经典教程《C++Primer》中有相关说明:

        使用了未定义行为的程序都是错误的,即使程序能够运行,也只是巧合。未定义行为源于编译器不能检测到的程序错误或太麻烦以至无法检测的错误。

        不幸的是,含有未定义行为的程序在有些环境或编译器中可以正确执行,但并不能保证同一程序在不同编译器中甚至在当前编译器的后继版本中会继续正确运行,也不能保证程序在一组输入上可以正确运行且在另一组输入上也能正确运行。

        程序不应该依赖未定义行为。

        同样地,通常程序不应该依赖机器相关的行为,比如假定int的位数是个固定且已知的值。我们称这样的程序是不可移植的。当程序移植到另外一台机器上时,要寻找并更改任何依赖机器相关操作的代码。在本来可以运行的程序中寻找这类问题是一项非常不愉快的任务。

 

        所以,对于文章开头那两个程序中的问题,我们实在不需要做过多考虑,而应该在实际编程中尽量避免,一旦使用“未定义行为”则可认为是bug。其实正常情况下,我们不会这样用,也不知道能否这样用,而这种“无知”恰恰也保护了我们。

0 0