尽管程序设计中不提倡使用goto语句,但是有的时候为了简化代码,难免会使用到goto。下面我要讨论的这个陷阱C语言程序中不会遇到,反而C++中稍不注意就会引起问题。
直接看以下代码:
int _tmain(int argc, _TCHAR* argv[]) { int t1 = 1; if (t1 >0) { goto __next; } int t2 = 5; __next: t2++; return 0; }
这段代码在vs2008,以及vs2013上都能顺利编过,甚至不会有任何警告。但如果你编译的是release版本程序,很遗憾,程序虽然能运行,但是t2中得到的值并不是我们想看到的6!
下面,看看这段代码不带优化编译的汇编程序:
int t1 = 1; 00961016 mov dword ptr [t1],1 if (t1 >0) 0096101D cmp dword ptr [t1],0 00961021 jle wmain+17h (961027h) 00961023 jmp __next (96102Eh) { goto __next; 00961025 jmp __next (96102Eh) } int t2 = 5; 00961027 mov dword ptr [t2],5 __next: t2++; 0096102E mov eax,dword ptr [t2] 00961031 add eax,1 00961034 mov dword ptr [t2],eax
很显然,原因是goto跳过了给t2赋值的代码,在对t2做自增运算之前t2中实际上是随机数(vs2013默认为1)。
c++中可以在一段代码中任意位置声明变量方便了编程,也可能带来问题。即使一个函数中的变量声明位置可以在任意位置,所有变量的空间分配仍然在函数执行之前就已经完成,然而对于变量内容的操作,却非要执行到具体的代码处才会完成。正如上面的goto语句引起的问题,虽然变量t2的空间已经存在,但是变量却没有被正确赋初值。
当然,如果把t2的类型换做一个class,情况就不会如此糟糕,因为编译的时候直接就会报错。原因和上面所述类似:虽然class的空间可以在函数执行之前分配,但是goto的存在使得class的构造函数不能被执行,编译器检测到使用没有执行构造函数的对象被使用,就会直接报错,编译会失败。(有不明真相的小朋友说报错是因为goto是c语言的东西,用了goto就必须把变量放到函数开头,显然是自己一厢情愿的想象而已。)
最后需要说明的是,如果编译debug版本的程序,这样的问题可以被很快排查,根据我的测试,在debug版本中,vc的编译器会生成相应的代码对于未初始化变量做检测,一旦对t2这样的变量做操作,程序就会报错。
原文地址:http://blog.csdn.net/yichigo/article/details/45077009