cc682/NetRoc
帮朋友调试了一个很奇怪的BUG,呵呵。稍微记录一下,再次提醒自己编译器是不可靠的。
代码类似这样:
DWORD dwSub0, dwSub1;
dwSub0 = dwSub1 = 0;
DWORD dwLoop1 = 0, dwLoop2 = 0;
for (dwLoop1 =0; dwLoop1 <7; ++ dwLoop1)
{
++dwSub0;
for (dwLoop2 =0; dwLoop2 <7; ++ dwLoop2)
{
++dwSub1;
//Do something
}
}
Debug情况下一切正常,执行完循环之后,dwSub0=7,deSub1=49。但是Release情况下,dwSub0和dwSub1的值都是7。
VC6编译器在特定情况下,优化的结果变为:
dwLoop1和dwLoop2都被初始化为0xFFFFFFA0,每次循环加上0x20,并且和0x80比较,用jb跳转。
最后跳转的地方汇编代码像这样:
eax, [esp+10h] ;[esp+10h]是dwLoop2
add edi, 328h
add eax, 20h
cmp eax, 80h
mov [esp+10h], eax
jb loc_54E19B ; ++dwSub1;问题来了!!!
mov ecx, [esp+1Ch] ;[esp+1Ch]是dwLoop1
add ecx, 20h
cmp edi, offset 00EFD564h
mov [esp+1Ch], ecx
jl loc_54E187 ; ++dwSub0;
因为是要循环7次,编译器把dwLoop2初始化为-60,每次加0x20,和0x80比较,正好是循环7次。这样估计是为了在接下来的代码里面重用dwLoop1和dwLoop2。不过使用JB指令来做跳转就发生错误了。由于JB指令只判断CF是否为1,是作无符号比较,所以这里产生了错误,内层循环每次都只会执行一次就退出到外层循环了。而正确的情况应该使用有符号的jl。
至此,问题暂时算是明了了。可能是在某些极特殊的情况下,VC的优化策略有些BUG。而且据朋友说在VC2003下面编译也有同样的错误,就是说可能在2003及以前的VC编译器中存在同样一个错误。2005之后的就不能确定了。
通过#pragma optimize(“”,off)、#pragma optimize(“”,on)关闭这个函数的优化之后,问题解决。
这个问题再次向我们提醒了一件事情,编译器也并不总是可靠的,往往我们在面对一些奇怪的无法解释的BUG的时候,不妨回过头来考虑是否是其他原因导致的,包括编译器、硬件、执行的软件环境,等等。