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

关于C++2005编译器的一个Bug。该怎么处理

2011-12-29 
关于C++2005编译器的一个Bug。关于C++2005编译器的一个Bug。大家一起确认一下我说的是否正确,也讨论一下用什

关于C++2005编译器的一个Bug。
关于C++2005编译器的一个Bug。

大家一起确认一下我说的是否正确,也讨论一下用什么好的方法来规避这个问题。
Contact me : chuyangguang@gmail.com 褚阳光

c++ 2005编译器在使用/clr选项时在一些特殊情况下会导致Release版本程序行为失常,产生错误的结果。
以下为示例程序:(项目类型为:VC ++ 2005的 CLR控制台应用程序)
#include "stdafx.h"
using namespace System;

typedef union
{
__int16 i;
unsigned __int16 ui;
} ALLTYPE;

void func(ALLTYPE *p);

int main(array<System::String ^> ^args)
{
ALLTYPE all;
all.ui = 32769;
func(&all);
Console::WriteLine("Press Enter to exit...");
Console::ReadLine();
return 0;
}

void func(ALLTYPE *p)
{
if(p->i == 0) //如果把这两行注释掉,
Console::WriteLine("0"); //则会正常工作。
if(p->ui == 32769)
Console::WriteLine("32769"); //func的函数体必须足够长,
else if(p->ui == 32770) //否则生成Release版本时会优化为inline函数。
Console::WriteLine("32770");
else if(p->ui == 32771)
Console::WriteLine("32771");
else if(p->ui == 32772)
Console::WriteLine("32772");
else if(p->ui == 32773)
Console::WriteLine("32773");
else if(p->ui == 32774)
Console::WriteLine("32774");
else if(p->ui == 32775)
Console::WriteLine("32775");
}
生成Release版本并运行,你会发现,程序的窗口中不会显示本应该有的32769。而Debug版本是能正常显示的。

问题出在Release版本的优化上。
来看Debug版本的汇编码如下: 

if(p->i == 0)  
00000000 push esi  
00000001 mov esi,ecx 
00000003 cmp dword ptr ds:[00C22DD8h],0 
0000000a je 00000011 
0000000c call 78E612A9 
00000011 cmp word ptr [esi],0 //判断 p->i == 0
00000015 jne 00000022 
Console::WriteLine("0");  
00000017 mov ecx,dword ptr ds:[023B3064h] 
0000001d call 7818BFA8 
if(p->ui == 32769)  
00000022 cmp word ptr [esi],8001h //判断 p->ui == 0
00000027 jne 00000037 
Console::WriteLine("32769");  
00000029 mov ecx,dword ptr ds:[023B3068h] 
0000002f call 7818BFA8 
00000034 nop  
00000035 jmp 000000B2 

上述汇编码没有使用优化处理,老老实实用 cmp word ptr操作,运行结果正常。

再看Release版本的汇编码:
if(p->i == 0)  
00000000 push edi  
00000001 push esi  
00000002 mov edi,ecx 
00000004 cmp dword ptr ds:[009E2DD8h],0 
0000000b je 00000012 
0000000d call 790A39D9 
00000012 xor esi,esi 
00000014 cmp word ptr [edi],0 //判断 p->i == 0
00000018 jne 00000025 
Console::WriteLine("0");  
0000001a mov ecx,dword ptr ds:[02193058h] 
00000020 call 783CE6D8 
if(p->ui == 32769)
00000025 movsx eax,word ptr [edi] //问题在这里,movsx指令
00000028 mov esi,eax //优化处理,把p->ui装入esi寄存器
0000002a cmp esi,8001h //比较结果为不相等!
00000030 jne 00000043 
Console::WriteLine("32769");  
00000032 mov ecx,dword ptr ds:[0219305Ch] 
00000038 call 783CE6D8 
0000003d nop  
0000003e jmp 000000C4 
else if(p->ui == 32770)  
00000043 cmp esi,8002h //优化后,后续比较直接使用esi寄存器的值
00000049 jne 00000059 
Console::WriteLine("32770");
0000004b mov ecx,dword ptr ds:[02193060h] 


00000051 call 783CE6D8 
00000056 nop  
00000057 jmp 000000C4

Release版本做了优化,看起来优化得还挺好,执行比较时直接使用esi寄存器的值,效率提高不少。但显然它比较错了,在0000002a处的cmp指令执行结果为不相等。问题就出在00000025处的movsx指令。movsx指令是有符号扩展,32769(8001h)传送到eax后变成了ffff8001h而不是00008001h。所以之后的cmp指令才认为与8001h不等。p->ui是无符号数类型,所以这里应该是movzx指令才对。

为什么会出现这种问题呢?来看Release的另外一个版本,去除func函数的头两行:
if(p->ui == 0) //如果把这两行注释掉,
Console::WriteLine("0"); //则会正常工作。
新的汇编码如下:
if(p->ui == 32769)
00000000 push edi  
00000001 push esi  
00000002 mov edi,ecx 
00000004 cmp dword ptr ds:[009E2DD8h],0 
0000000b je 00000012 
0000000d call 790A39D9 
00000012 xor esi,esi 
00000014 movzx eax,word ptr [edi] //传送到eax,此时使用了movzx
00000017 mov esi,eax 
00000019 cmp esi,8001h 
0000001f jne 00000032 
Console::WriteLine("32769");  
00000021 mov ecx,dword ptr ds:[02193058h] 
00000027 call 783CE6D8 
0000002c nop  
0000002d jmp 000000B3
这一次使用了movzx指令,所以运行结果也变得正常了。

进一步测试发现,如果union的第一个成员是int 而非 __int16程序也运行正常。
而对于以下代码:
if(p->ui == 0)  
Console::WriteLine("0");  
if(p->i == -1)
Console::WriteLine("-1");  
p->i也无法与-1判断为相等。


综上所述,c++ 2005 编译器使用/clr选项生成Release版本时,处理union的成员符号类型的行为可以这样描述:

在一段被优化(Release)的代码上,如果union具有两个字节数相同但符号类型不同的成员,靠后使用的成员不再具有本来的符号类型,而是与该union在这段代码上第一次使用的成员的符号类型一样。

这个问题导致Debug与Release版本行为不一致,同样也造成Release版本程序行为的不确定性。我个人觉得还是比较棘手的,大家看有什么好的办法,或者微软有没有就这个问题给出过解释?


[解决办法]
关注 一下,不知道2008有没有这个问题
[解决办法]
up
[解决办法]
UP
[解决办法]
... 学习了
[解决办法]
O_O
[解决办法]
强,学习了
[解决办法]
MARK
关注
[解决办法]
学习了
[解决办法]
如果作嵌入式,volatile很常用。
[解决办法]
2005下确实有Release版本出错问题,做托管代码Webservice 时曾遇到!
[解决办法]
MARK
[解决办法]
学习
[解决办法]
报告于ms
[解决办法]
学了哈
[解决办法]
Good job!
[解决办法]
强, 学习了
[解决办法]

探讨
强, 学习了

[解决办法]
mark


[解决办法]
高手啊,,,佩服
[解决办法]
MARK,学习了!

热点排行