//========================================================================
//TITLE:
// VS2005和EVC4字符串宏在MIPSII架构的比较
//AUTHOR:
// norains
//DATE:
// Friday 21-December-2007
//Environment:
// EVC4.0 + SDK-WINCE5.0-MIPSII
// VS2005 + SDK-WINCE5.0-MIPSII
//========================================================================
今天调试程序时,遇到一个摸不着头绪的问题.同样的代码,在EVC4.0编译之后能正常运行,而在VS2005中也能正常通过编译,但偏偏一运行,就提出内存越界错误.
提炼之后的简单代码如下:
TCHARszBuf[MAX_PATH]={0};
#defineSTR_DEFTEXT("DEFINEApp")
_tcscpy(szBuf,_tcslwr(STR_DEF));
现在我们来剖析一下.
在此之前来说明一下实验环境:
VS2005 英文版和EVC4.0,选择的都采用MIPSII进行DEBUG版本的编译.需要注意的是,因为编译器的版本实现问题,无法保证本文的结果能在其它编译器(如ARM,模拟器等)中重现. 先来一段简单的代码做范例:
intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
#defineSTR_DEFTEXT("DEFINEApp")
STR_DEF[0]='G';
return0;
}
这段代码在EVC4.0下顺利编译通过,但在vs2005就出现编译错误:error C2166: l-value specifies const object. 很明显,在vs2005中将STR_DEF作为const字符串,所以STR_DEF[0] = 'G'这语句肯定会出错.换句话说,EVC4.0版本中并没有将STR_DEF当成const变量.现在我们不去考虑vs2005,只在evc4.0范围中考虑:更改了字符之后,原宏定义会不会受到影响?在代码中增添一句显示如下:
intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
#defineSTR_DEFTEXT("DEFINEApp")
STR_DEF[0]='G';
MessageBox(NULL,STR_DEF,TEXT(""),MB_OK);
return0;
}
显示的对话框如下(图一):
我们可以看出,在MessageBox()中的STR_DEF宏并没有受之前赋值的影响.此STR_DEF非彼之STR_DEF.
回到文章开头的_tcslwr函数,我们将代码更改如下:
intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
#defineSTR_DEFTEXT("DEFINEApp")
MessageBox(NULL,_tcslwr(STR_DEF),TEXT(""),MB_OK);
return0;
}
幸运的是,这段更改后的代码,无论是在vs2005还是在evc4.0中都能正确编译通过;不幸的是,编译出来的程序运行结果完全不同:
EVC4.0编译的程序运行截图(图二):
从图上我们可以看出,STR_DEF已经完美的转化为小写,并且很清晰明了的显示了出来.
那么vs2005编译出来的程序情况如何呢?截图如下(图三)
异常0xC0000005 ! 最熟悉不过的异常了,内存溢出!
先将这个异常放一放,我们先来看看_tcslwr函数的定义:
TCHAR *_tcslwr( TCHAR *string );
最关键的是返回值的描述:
Both of these functions return a pointer to the converted string. Because the modification is done in place, the pointer returned is the same as the pointer passed as the input argument. No return value is reserved to indicate an error.
这段英文的大意是:返回的是指向转换后的字符串的指针.因为这转换是在内部进行的,所以这转换后的指针和传入的指针地址是相同的.
回头再来看看个函数调用:_tcslwr(STR_DEF).按之前的说明,那么调用_tcslwr之后返回的应该是STR_DEF的指针!如果从函数来看,_tcslwr的形参不是const,那么vs2005应该无法编译通过才是,难道vs2005在编译时不符合C++规范?为了验证这点,我们先写一个空函数测试一下:
TCHAR*Test(TCHAR*pStr)
{
returnpStr;
}
intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
#defineSTR_DEFTEXT("DEFINEApp")
MessageBox(NULL,_tcslwr(STR_DEF),TEXT(""),MB_OK);
Test(STR_DEF);//居然可以编译通过
return0;
}
我的天呐,VS2005究竟将字符串宏解释成什么了?因为如果有这种代码,vs2005中是肯定通不过的(当然evc4.0也不行):
constTCHARcszStr[10]={0};
Test(cszStr);//无法编译通过
难道这时候vs2005又把字符串宏不当成const了?这不知道算不算vs2005的一个bug?
但这还是无法解释为何同样的代码在vs2005和evc4.0编译之后的运行结果会迥然不同.我们再把代码更改一下,看看会出现什么结果:
TCHAR*Test(TCHAR*pStr)
{
returnpStr;
}
intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
#defineSTR_DEFTEXT("DEFINEApp")
TCHAR*pReturn=Test(STR_DEF);
pReturn[0]='A';
return0;
}
这段代码VS2005和EVC4.0中都能顺利编译通过,但如果采用单步调试方式,我们将发现pReturn[0] = 'A'这语句在VS2005中出错!而与此相反,该语句却能在EVC4.0中畅行无阻.根据以上的情况,我们完全有理由可以大胆推测,造成本文开头所示的错误的一个最重要原因是:临时变量的生存周期不同.
还是以刚刚那段代码为例子.TCHAR *pReturn = Test(STR_DEF)这段代码构建了一个临时变量(在这里我们暂时称之为pStr),而pStr在vs2005的生存周期是在函数Test()体内,而在evc中却是在WinMain()函数!所以就不难解释为何通过返回指针赋值(即代码中的pReturn[0] = 'A'),在两种编译器中有完全不同的表现了!
这不由得让我们想起了一个经典的代码:
for(int i = 0; i < 10; i ++)
{}
由于对C++标准的支持不同,所以导致变量i的生存周期在不同的编译器中可能是不同的!(根据C++99标准,i生命周期应该只存在于循环体内)
虽然是如此猜测,但实际上是否如此呢?分别设置vs2005和evc,让其产生cod文件,通过查看cod的汇编代码来证明我们的推测:
VS2005编译Test函数的汇编代码:
?Test@@YAPA_WPA_W@Z:#Test
.setnoreorder
$LN5@Test:
00000fff827bdaddiusp,sp,0xFFF8
00004.framesp,8,RA#0x00000008
00004.mask0x00000000,0x00000000#(0)
00004.fmask0x00000000,0x00000000#(0)
00004.prologue0
$M28923:
000040008afa4swa0,pStr$(sp)
0000800088fa2lwv0,pStr$(sp)
0000c0000afa2swv0,$T28922(sp)
EVC4.0编译Test函数的汇编代码:
pStr$=8
$T27371=0
?Test@@YAPAGPAG@Z:#Test
.setnoreorder
00000fff827bdaddiusp,sp,0xFFF8
00004.framesp,8,RA#0x00000008
00004.mask0x00000000,0x00000000#(0)
00004.fmask0x00000000,0x00000000#(0)
00004.prologue0
$M27372:
000040008afa4swa0,pStr$(sp)
0000800088fa2lwv0,pStr$(sp)
0000c0000afa2swv0,$T27371(sp)
0001000008fa2lwv0,$T27371(sp)
看到了么?函数的汇编代码基本相同,但却有一点点小差异,而这小差异,正是导致vs2005和evc4.0编译的程序运行状态完全迥异的原因!
这一小差异,就是evc在函数体外定义了pStr$(指的是 pStr$ = 8 这语句段)! 也就是说,pStr$的生命周期是全局的,而这恰好印证了我们之前的推测.
根据C++99标准,vs2005的行为才是最符合的.在把项目从evc4.0迁移到vs2005过程中,如果出现之前尚未显现过的异常情况,不妨从标准入手,说不定会有意外的收获.
分享到:
相关推荐
EVC下UNICODE的字符串的输出问题
visual Assist X 10.4配合注册机已经上传。应经测试可以使用
基于evc的wince串口接收发送的程序和源代码,适用于初级接触串口编写的人
在S32410下直接运行的串口通信程序在S32410中利用EVC编写的串口通信源码
自己写得EVC4串口测试程序,非常好用,希望大家喜欢!希望能对大家有点用处!
EVC程序转VS2005时会出现一些问题,该文档介绍了怎么转换以及转换过程中出现问题怎么解决
evc 4安装的序列号是sn:TRT7H-KD36T-FRH8D-6QH8P-VFJHQ
eVC4.eVC4.eVC4.eVC4.eVC4.
1. Platform Builder 5.0的安装 2. Platform Builder 5.0 补丁2007,...4. EVC的Service Pack 4的安装 5. 安装EVC的标准软件开发包Standard SDK 6. Microsoft Activesync 4.5的安装 7. Visual Studio 2005的安装
WINCE BUTTON STYLE EXAMPLE BASED ON VS2005 WinCE Button XP 风格 CButtonST VS2005 EVC
ecv编程的帮助文件,有关于串口的
基于evc的wince串口接收发送程序,适用于evc++4.0
SDK程序改写而来的MFC类,希望能和致力于WINCE开发的朋友多多交流,由于本人才疏学浅,程序中有许多不完善的地方,请大家指正。我的程序是基于“主动发送请求,被动接收响应”的假设,因此我只设置了一个接收数据的...
EVC串口操作程序 串口初始化\ 串口输出 串口打印
visual Assist X 注册 10.4版已经测试过了。 可以正常使用10.4版本的。助手也上穿了。
EVC4上面在程序转换到vs 2005(vs 2008)经常遇到的错误。
EVC 串口通信代码 EVC 串口通信代码
EVC工程移植到VS2005的经验,详细介绍了如何将原有evc工程一直到VS2005上,方便用户项目升级。
串口调试软件 方便程序员或调试人员对底层硬件驱动程序的串口通信数据的调试检测
描述了evc 到vs2005代码转换的一些问题, 方便vs2005下的嵌入式程序开发