C++中的goto陷阱

尽管程序设计中不提倡使用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这样的变量做操作,程序就会报错。

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。