首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

VS2010中的printf有bug,该如何解决

2012-09-29 
VS2010中的printf有bug小弟学过一点16位汇编,现在刚学了2天C语言,根据朋友反映,在VS2010中发现如下问题,由

VS2010中的printf有bug
小弟学过一点16位汇编,现在刚学了2天C语言,根据朋友反映,在VS2010中发现如下问题,由于我系C刚学2天的新手,有分析不到位的地方,请大侠们批评指正。

printf("%d,%d,%d,%d,%d,%d\t\t",++i,--i,i++,i--,-i++,-i--);
在这个函数中,若设 i初始为8
则输出结果为 8,8,7,8,-7,-8 

我们在调试中,将printf("%d,%d,%d,%d,%d,%d\t\t",++i,--i,i++,i--,-i++,-i--);反汇编,得到如下代码:
011C34A5 mov eax,dword ptr [i]  
011C34A8 neg eax  
011C34AA mov dword ptr [ebp-0D0h],eax  
011C34B0 mov ecx,dword ptr [i]  
011C34B3 sub ecx,1  
011C34B6 mov dword ptr [i],ecx
  
011C34B9 mov edx,dword ptr [i]  
011C34BC neg edx  
011C34BE mov dword ptr [ebp-0D4h],edx  
011C34C4 mov eax,dword ptr [i]  
011C34C7 add eax,1  
011C34CA mov dword ptr [i],eax
  
011C34CD mov ecx,dword ptr [i]  
011C34D0 mov dword ptr [ebp-0D8h],ecx  
011C34D6 mov edx,dword ptr [i]  
011C34D9 sub edx,1  
011C34DC mov dword ptr [i],edx
  
011C34DF mov eax,dword ptr [i]  
011C34E2 mov dword ptr [ebp-0DCh],eax  
011C34E8 mov ecx,dword ptr [i]  
011C34EB add ecx,1  
011C34EE mov dword ptr [i],ecx 

 
011C34F1 mov edx,dword ptr [i]  
011C34F4 sub edx,1  
011C34F7 mov dword ptr [i],edx  
011C34FA mov eax,dword ptr [i]  
011C34FD add eax,1  
011C3500 mov dword ptr [i],eax  
011C3503 mov esi,esp  
011C3505 mov ecx,dword ptr [ebp-0D0h] 8
011C350B push ecx  
011C350C mov edx,dword ptr [ebp-0D4h]  
011C3512 push edx  
011C3513 mov eax,dword ptr [ebp-0D8h]  
011C3519 push eax  
011C351A mov ecx,dword ptr [ebp-0DCh]  
011C3520 push ecx  
011C3521 mov edx,dword ptr [i]  
011C3524 push edx  
011C3525 mov eax,dword ptr [i]  
011C3528 push eax  
011C3529 push offset string "%d\n%d\n%d\n" (11C59F8h)  
011C352E call dword ptr [__imp__printf (11C82B0h)]  
011C3534 add esp,1Ch  
011C3537 cmp esi,esp  
011C3539 call @ILT+295(__RTC_CheckEsp) (11C112Ch) 

分析如下:
从反汇编代码来看,vs2010中对printf函数的上述参数中,是从右到左运算的,即先运算-i--,然后-i++,依次到左,对上述汇编代码分别分析如下
(1) -i-- 
011C34A5 mov eax,dword ptr [i]  
011C34A8 neg eax  
011C34AA mov dword ptr [ebp-0D0h],eax  
011C34B0 mov ecx,dword ptr [i]  
011C34B3 sub ecx,1  
011C34B6 mov dword ptr [i],ecx
先求变量的相反数,保存变量用于输出,然后将初始变量-1(i=7),保存到变量里,用于下次计算;
(2)-i++
011C34B9 mov edx,dword ptr [i]  
011C34BC neg edx  
011C34BE mov dword ptr [ebp-0D4h],edx  
011C34C4 mov eax,dword ptr [i]  
011C34C7 add eax,1  
011C34CA mov dword ptr [i],eax 
同样道理,无非最后是+1,i=8
(3)i--
011C34CD mov ecx,dword ptr [i]  
011C34D0 mov dword ptr [ebp-0D8h],ecx  
011C34D6 mov edx,dword ptr [i]  
011C34D9 sub edx,1  
011C34DC mov dword ptr [i],edx  
上一步的i=8吧,这里就是,将8保存用于显示,然后-1,保存用于下次运算(i=7)
(4)i++
011C34DF mov eax,dword ptr [i]  
011C34E2 mov dword ptr [ebp-0DCh],eax  
011C34E8 mov ecx,dword ptr [i]  
011C34EB add ecx,1  
011C34EE mov dword ptr [i],ecx 


保存上次的7用于显示输出,后+1(i=8)用于下次运算
(5)011C34F1 mov edx,dword ptr [i]  
011C34F4 sub edx,1  
011C34F7 mov dword ptr [i],edx  
011C34FA mov eax,dword ptr [i]  
011C34FD add eax,1  
011C3500 mov dword ptr [i],eax
问题就出在这里了,这里它把第一、第二2个一起运算了,而且都保存在地址[i]里,因此造成第一、第二两个值一样,而本应该是不一样的。问题就在于没有象前面那样单独保存到一个独立的内存地址中去,因此在后面压栈后调用显示函数时,造成第一和第二个数值一致了。(PS:而且其在压栈时,对第一个、第二个数值结果也是压栈同一个内存地址的数值)

我觉得这不是计算顺序的问题,而是一个bug问题了。按从右到左来计算的话,正确的值应该是:
8,7,7,8,-7,-8

[解决办法]
同一系统的不同版本完全可以采用不同方式;同一版本在不同优化方式下,在不同位置都可能采用不同顺序。因为这些做法都符合语言规范。在这里还要注意顺序点的问题:即使某一边的表达式先算了,其副作用也可能没有反映到内存,因此对另一边的计算没有影响。

实际应该说的是:这种东西根本不该写,讨论其效果没有意义。


LZ应该看看6楼发的链接。他的主要意思是,这种代码编译器是知道会可能会出现错误了(而不是不知道而发生的BUG),你的汇编代码只是印证了这种错误确实存在。所以这种代码本来就不该存在……
[解决办法]
楼主你知道为什么编译器要把后面的计算结果放到一个独立的内存, 而前面两个没有吗?
因为前面两个是前加, 后面四个是后加!
它们不只是返回值不一样而已, 而是其实现原理也不一样, 我们可以通过重载前置加后后置加的方式看出来:

前置加法:

C/C++ code
Carrot& Carrot::operator++(){     (*this) += 1;    // or other implement    return *this;}
[解决办法]
不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!

热点排行