C++低级错误案例整理V1x.docx
《C++低级错误案例整理V1x.docx》由会员分享,可在线阅读,更多相关《C++低级错误案例整理V1x.docx(15页珍藏版)》请在冰点文库上搜索。
C++低级错误案例整理V1x
C++低级错误案例目录
1前言
本规范用于规范C++项目组中各个局点交付版本的代码交付质量。
保证系统的质量,保证系统有效的运行。
提高编码工作效率,规避低级错误出现,指导代码评审活动开展工作。
代码案例:
2【低级错误】
2.1数组下标访问越界
(1)数组下标根据计算得出
i=a–b;
array[i]=0;//使用前应该检查i的合法性
(2)数组下标通过函数得出
voidmain()
{
inti,b[10];
getIndex(&i);
b[i]=0;//使用前应该检查i的合法性
}
(3)数组下标是循环变量
inti,max,b[10];
getMax(&max);
for(i=0;i{b[i]=0;}}2.2变量在定义时同时未初始化错误:inti;//没有赋初始值printf("i=%d\n",i);//随机分配一个值:-858993460自动对象的存储分配发生在定义它的函数被调用时,未初始话化的自动对象包含一个随机的位模式,是该存储区上次被使用的结果,值是不确定的。正确:inti=0;//定义的同时赋初始值02.3在循环条件中求循环次数或者大小,影响性能错误:for(inti=0;i<(int)attributeList.length();i++){……}正确:inttmp_iListLength=attributeList.length()for(inti=0;i{……}此案例发生在广东移动业务中,数据量达5000千万用户,影响性能是秒级的,在呼叫业务中用户是不可接受的。2.4混淆“=”与“==”如下例子的循环判断条件中,将”==”误写成”=”,导致死循环。错误:Boolresult=True;while(result=True){result=execFunc();wait();}正确:while(True==result){result=execFunc();wait();}编程规范要求将常量写在等号左边(如“True==result”),确保编译时即可发现错误。2.5指针使用前必须做非空检查判断CAppService*tmp_pAppService=dynamic_cast(m_pAppSession->getAppServiceBase());m_pLogger=tmp_pAppService->getLogger();//tmp_pAppService指针有可能为空,那么就会core掉正确:CAppService*tmp_pAppService=NULL;tmp_pAppService=dynamic_cast(m_pAppSession->getAppServiceBase());if(tmp_pAppService!=NULL){m_pLogger=tmp_pAppService->getLogger();}else{错误处理;}类似数据库查询对结果集读取也应该先检查再使用如:ENIP::IMemRecordSet*tmp_pRecordSet=t_dataSetRef.getRecordSet();if(NULL==tmp_pRecordSet){ELOG("RecordSetpointerisNULL.");t_bHasRecord=ENIP::False;}elseif(tmp_pRecordSet->eof()){LOG("NorecordfoundinLostCallServiceBlackList.");t_bHasRecord=ENIP::False;}在函数或方法中涉及指针或引用传递须遵循以下规则:1.函数属于私有类型,如果内部不做检查,外部调用时必须做检查(函数内部需要加以说明);如果内部做了检查,外部调用时可以不做检查(调用时需要加以说明);2.函数属于公有类型,函数内部必须做检查,在外部调用时可以不做检查(调用时需要加以说明);2.6宏定义未充分封装替换参数错误:#defineRECT_SIZE(X*Y)X=length+1;Y=width+1;最终结果为length+width+1,与预计不符合正确:#defineRECT_SIZE((X)*(Y))2.7case语句遗漏break或缺少default分支条件错误:caseWM_CLOSE:Close();//遗漏break语句,执行完关闭后,重新使用已释放资源caseWM_READ:Read();break; 正确:switch(){caseWM_CLOSE:Close();break;caseWM_READ:Read();break;…default://不要遗漏default语句…}2.8未判断函数返回值错误://处理计费矩阵查询结果doMatrixAnalysis(request,response)以上代码忽略了函数的返回值检查,调用计费矩阵分析方法,函数原型如下:intdoMatrixAnalysis(constTiChargeMatrixAnalysisReqArg&reqArg,TiChargeMatrixAnalysisResArg&resArg);如此,有可能在后续计费执行过程出现严重错误……正确:if(success==doMatrixAnalysis(request,response)){….//计费矩阵分析成功}else{ELOG("doMatrixAnalysisfailureandreleasethecall");t_bIsError=ENIP::True;//计费矩阵分析失败,异常退出break;}2.9判断无符号数是否小于0unsignedcharc;//c赋值为具体的循环次数,会导致死循环while(c-->=0){//dosomething…} 3【1级错误】3.1使用野指针(1)使用未分配空间的指针voidfunc(){char*p;if(NULL!=p){printf(“%s”,p);}}(2)内存空间释放后指针未置Null,内存指针仍被继续使用void*g_pBuf=NULL;voidATM_CellRecv(U8*pBuf,U32ulLen){g_pBuf=pBuf;//g_pBuf通过pBuf赋值指向内存区域......if(NULL!=pBuf){free(pBuf);//只是释放了内存,而g_pBuf并没有置成NULL.}}voidfunc2(){g_pBuf=pBuf;if(NULL!=g_pBuf){//赋值操作,将导致非法内存改写}} 3.2内存拷贝忽略字符串结尾标志’\0’错误:voidfun(){chardest[10];charsrc[]="0123456789";memcpy(dest,src,sizeof(src));} 正确:voidfun(){charsrc[]="0123456789";intiLenght=sizeof(src);char*dest=newchar[iLenght];memcpy(dest,src,iLenght);deletepdest;}字符串结束符相关有许多安全函数可供调用:snprintf,strncpy,strncat,safecopy等等,不允许使用非安全函数:sprint,strcpy3.3分支流程未释放动态申请的内存voidFunction1(intnSize){char*p=(char*)malloc(nSize);if(!GetStringFrom(p,nSize)){MessageBox(“Error”); return;}…//usingthestringpointedbyp;free(p);}当函数GetStringFrom()返回零的时候,指针p指向的内存就不会被释放。这是一种常见的发生内存泄漏的情形。程序在入口处分配内存,在出口处释放内存,但是c函数可以在任何地方退出,所以一旦有某个出口处没有释放应该释放的内存,就会发生内存泄漏。内存分配方式有三种: (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量,静态内存区。 (2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 (3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc申请任意多少的内存,程序员自己负责在何时用free释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。应用程序一般使用malloc,realloc等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。3.4数据类型不一致,变量或参数赋值出错shortintx;inty;void*p ;p=&x;*(int*)p=y;x只有两个字节的空间,而int需要4个字节的空间,实际上己出问题,编译器未设定字节对齐时会出错,在linux下不会出现入参被踩。还有一个例子,会导致入参被踩:intfunc(shortints,int*pi){…//dosomething*pi=0;return0;}intmain(intargc,char*argv[]){shortintx;shortinty;intiRet;…//dosomethingx=1;iRet=func(x,(int*)&y);}调用函数func后,x=0。3.5循环体内改写循环变量unsignedlongi;for(i=0;i<1024;c++){for(i=0;i<512;c++)//尽量避免在循环中修改循环变量{//dosomething}}3.6魔鬼数字如:0x01,“|”,“”,“B-DISP”;错误:if("B-DISP"==attributeList[i].attributeType){……..}正确:将字符串"B-DISP"在*.h文件中定位为字符串常量ECC_LDAPATTRS_B-DISPconstTStringECC_LDAPATTRS_B-DISP="B-DISP";if(ECC_LDAPATTRS_B-DISP==attributeList[i].attributeType){……..}3.7对只读参数未加const修饰错误:voidFunc(char*src,char*dst,intlen){…strncpy(src,dst,len);//误操作将src的值改写}正确:voidFunc(constchar*src,char*dst,intlen){…strncpy(dst,src,len);} 3.8各种资源句柄没有释放FILE*fp1=fopen(“file”,w+);....Return0;首次打开文件,使用后未关闭文件句柄,执行不成功。 正确:FILE*fp2=fopen(“file”,a+);......if(NULL!=fp2){fclose(fp2);Return0;}Else{Return-1;}CString中GetBuffer和ReleaseBuffer的使用;信号量、临界区和互斥锁等同步对象资源;线程进程资源;3.9变量名、枚举名、常量名字面意思、注释含义与使用时相反CVACSubScirbeReq中字段:CMsgFieldIntm_isNeedNotifySP;//CRM侧订购的是否需要通知SP该字段本意是个枚举值(ENUM),取值有2个:enumisNotifySP{Notify_SP_NO=1,//不通知Notify_SP_YES=2//通知SP};//下面的逻辑是需要通知sp才处理的,枚举含义和实际意义完全颠倒if(From_CRM==pSessionInfo->m_reqInfo.m_CRMOrderFlag.asInt()&&Notify_SP_NO==pSessionInfo->m_reqInfo.m_isNeedNotifySP.asInt())3.10所有的可能抛异常的地方都要有捕获constchar*sql="selectmsisdn,serviceid,spid,billmonth,amount,account,rewardAmount,rewardAccount,realaccountfromhistoryrecordwheremsisdn=:v1andserviceid=:v2andspid=:v3";TINTnCommandIndex=m_pDbAgent->excute(sql,msisdn,pa,paCount);if(nCommandIndex==Failed){returnSearchResult_DBError;}CRecordSetrs;rs.setSACommand(&CDBAgent::getInstance()->s_commandSet[nCommandIndex]);//完全没有try和catch保护,一旦数据库出错就是一个core4【2级错误】4.1循环变量数据类型太小unsignedcharc;unsignedshorts;//dosomething//s>255for(c=0;c{//dosomething}4.2函数局部变量或参数过大,堆栈溢出voidFunction(void){charb[0x200000];}intmain(){ret=function();}函数参数的输入、输出如果是一个比较大的结构,要用指针带入保存输入数据的内存地址,用指针带入保存输出的内存地址,不要直接用值传递的方式输入和输出。如果函数的较大的输入输出使用值传递方式,对函数堆栈处理有较大的开销。当数据接近或者大于堆栈空间的时候,就会出现堆栈溢出错误如果是使用DOPRA平台,可以在v_configkernel.h文件中找到默认的栈大小#defineVOS_DEFAULT_STACK_SIZE0x200000
b[i]=0;
2.2变量在定义时同时未初始化
错误:
inti;//没有赋初始值
printf("i=%d\n",i);//随机分配一个值:
-858993460
自动对象的存储分配发生在定义它的函数被调用时,未初始话化的自动对象包含一个随机的位模式,是该存储区上次被使用的结果,值是不确定的。
正确:
inti=0;//定义的同时赋初始值0
2.3在循环条件中求循环次数或者大小,影响性能
for(inti=0;i<(int)attributeList.length();i++)
……
inttmp_iListLength=attributeList.length()
for(inti=0;i{……}此案例发生在广东移动业务中,数据量达5000千万用户,影响性能是秒级的,在呼叫业务中用户是不可接受的。2.4混淆“=”与“==”如下例子的循环判断条件中,将”==”误写成”=”,导致死循环。错误:Boolresult=True;while(result=True){result=execFunc();wait();}正确:while(True==result){result=execFunc();wait();}编程规范要求将常量写在等号左边(如“True==result”),确保编译时即可发现错误。2.5指针使用前必须做非空检查判断CAppService*tmp_pAppService=dynamic_cast(m_pAppSession->getAppServiceBase());m_pLogger=tmp_pAppService->getLogger();//tmp_pAppService指针有可能为空,那么就会core掉正确:CAppService*tmp_pAppService=NULL;tmp_pAppService=dynamic_cast(m_pAppSession->getAppServiceBase());if(tmp_pAppService!=NULL){m_pLogger=tmp_pAppService->getLogger();}else{错误处理;}类似数据库查询对结果集读取也应该先检查再使用如:ENIP::IMemRecordSet*tmp_pRecordSet=t_dataSetRef.getRecordSet();if(NULL==tmp_pRecordSet){ELOG("RecordSetpointerisNULL.");t_bHasRecord=ENIP::False;}elseif(tmp_pRecordSet->eof()){LOG("NorecordfoundinLostCallServiceBlackList.");t_bHasRecord=ENIP::False;}在函数或方法中涉及指针或引用传递须遵循以下规则:1.函数属于私有类型,如果内部不做检查,外部调用时必须做检查(函数内部需要加以说明);如果内部做了检查,外部调用时可以不做检查(调用时需要加以说明);2.函数属于公有类型,函数内部必须做检查,在外部调用时可以不做检查(调用时需要加以说明);2.6宏定义未充分封装替换参数错误:#defineRECT_SIZE(X*Y)X=length+1;Y=width+1;最终结果为length+width+1,与预计不符合正确:#defineRECT_SIZE((X)*(Y))2.7case语句遗漏break或缺少default分支条件错误:caseWM_CLOSE:Close();//遗漏break语句,执行完关闭后,重新使用已释放资源caseWM_READ:Read();break; 正确:switch(){caseWM_CLOSE:Close();break;caseWM_READ:Read();break;…default://不要遗漏default语句…}2.8未判断函数返回值错误://处理计费矩阵查询结果doMatrixAnalysis(request,response)以上代码忽略了函数的返回值检查,调用计费矩阵分析方法,函数原型如下:intdoMatrixAnalysis(constTiChargeMatrixAnalysisReqArg&reqArg,TiChargeMatrixAnalysisResArg&resArg);如此,有可能在后续计费执行过程出现严重错误……正确:if(success==doMatrixAnalysis(request,response)){….//计费矩阵分析成功}else{ELOG("doMatrixAnalysisfailureandreleasethecall");t_bIsError=ENIP::True;//计费矩阵分析失败,异常退出break;}2.9判断无符号数是否小于0unsignedcharc;//c赋值为具体的循环次数,会导致死循环while(c-->=0){//dosomething…} 3【1级错误】3.1使用野指针(1)使用未分配空间的指针voidfunc(){char*p;if(NULL!=p){printf(“%s”,p);}}(2)内存空间释放后指针未置Null,内存指针仍被继续使用void*g_pBuf=NULL;voidATM_CellRecv(U8*pBuf,U32ulLen){g_pBuf=pBuf;//g_pBuf通过pBuf赋值指向内存区域......if(NULL!=pBuf){free(pBuf);//只是释放了内存,而g_pBuf并没有置成NULL.}}voidfunc2(){g_pBuf=pBuf;if(NULL!=g_pBuf){//赋值操作,将导致非法内存改写}} 3.2内存拷贝忽略字符串结尾标志’\0’错误:voidfun(){chardest[10];charsrc[]="0123456789";memcpy(dest,src,sizeof(src));} 正确:voidfun(){charsrc[]="0123456789";intiLenght=sizeof(src);char*dest=newchar[iLenght];memcpy(dest,src,iLenght);deletepdest;}字符串结束符相关有许多安全函数可供调用:snprintf,strncpy,strncat,safecopy等等,不允许使用非安全函数:sprint,strcpy3.3分支流程未释放动态申请的内存voidFunction1(intnSize){char*p=(char*)malloc(nSize);if(!GetStringFrom(p,nSize)){MessageBox(“Error”); return;}…//usingthestringpointedbyp;free(p);}当函数GetStringFrom()返回零的时候,指针p指向的内存就不会被释放。这是一种常见的发生内存泄漏的情形。程序在入口处分配内存,在出口处释放内存,但是c函数可以在任何地方退出,所以一旦有某个出口处没有释放应该释放的内存,就会发生内存泄漏。内存分配方式有三种: (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量,静态内存区。 (2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 (3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc申请任意多少的内存,程序员自己负责在何时用free释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。应用程序一般使用malloc,realloc等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。3.4数据类型不一致,变量或参数赋值出错shortintx;inty;void*p ;p=&x;*(int*)p=y;x只有两个字节的空间,而int需要4个字节的空间,实际上己出问题,编译器未设定字节对齐时会出错,在linux下不会出现入参被踩。还有一个例子,会导致入参被踩:intfunc(shortints,int*pi){…//dosomething*pi=0;return0;}intmain(intargc,char*argv[]){shortintx;shortinty;intiRet;…//dosomethingx=1;iRet=func(x,(int*)&y);}调用函数func后,x=0。3.5循环体内改写循环变量unsignedlongi;for(i=0;i<1024;c++){for(i=0;i<512;c++)//尽量避免在循环中修改循环变量{//dosomething}}3.6魔鬼数字如:0x01,“|”,“”,“B-DISP”;错误:if("B-DISP"==attributeList[i].attributeType){……..}正确:将字符串"B-DISP"在*.h文件中定位为字符串常量ECC_LDAPATTRS_B-DISPconstTStringECC_LDAPATTRS_B-DISP="B-DISP";if(ECC_LDAPATTRS_B-DISP==attributeList[i].attributeType){……..}3.7对只读参数未加const修饰错误:voidFunc(char*src,char*dst,intlen){…strncpy(src,dst,len);//误操作将src的值改写}正确:voidFunc(constchar*src,char*dst,intlen){…strncpy(dst,src,len);} 3.8各种资源句柄没有释放FILE*fp1=fopen(“file”,w+);....Return0;首次打开文件,使用后未关闭文件句柄,执行不成功。 正确:FILE*fp2=fopen(“file”,a+);......if(NULL!=fp2){fclose(fp2);Return0;}Else{Return-1;}CString中GetBuffer和ReleaseBuffer的使用;信号量、临界区和互斥锁等同步对象资源;线程进程资源;3.9变量名、枚举名、常量名字面意思、注释含义与使用时相反CVACSubScirbeReq中字段:CMsgFieldIntm_isNeedNotifySP;//CRM侧订购的是否需要通知SP该字段本意是个枚举值(ENUM),取值有2个:enumisNotifySP{Notify_SP_NO=1,//不通知Notify_SP_YES=2//通知SP};//下面的逻辑是需要通知sp才处理的,枚举含义和实际意义完全颠倒if(From_CRM==pSessionInfo->m_reqInfo.m_CRMOrderFlag.asInt()&&Notify_SP_NO==pSessionInfo->m_reqInfo.m_isNeedNotifySP.asInt())3.10所有的可能抛异常的地方都要有捕获constchar*sql="selectmsisdn,serviceid,spid,billmonth,amount,account,rewardAmount,rewardAccount,realaccountfromhistoryrecordwheremsisdn=:v1andserviceid=:v2andspid=:v3";TINTnCommandIndex=m_pDbAgent->excute(sql,msisdn,pa,paCount);if(nCommandIndex==Failed){returnSearchResult_DBError;}CRecordSetrs;rs.setSACommand(&CDBAgent::getInstance()->s_commandSet[nCommandIndex]);//完全没有try和catch保护,一旦数据库出错就是一个core4【2级错误】4.1循环变量数据类型太小unsignedcharc;unsignedshorts;//dosomething//s>255for(c=0;c{//dosomething}4.2函数局部变量或参数过大,堆栈溢出voidFunction(void){charb[0x200000];}intmain(){ret=function();}函数参数的输入、输出如果是一个比较大的结构,要用指针带入保存输入数据的内存地址,用指针带入保存输出的内存地址,不要直接用值传递的方式输入和输出。如果函数的较大的输入输出使用值传递方式,对函数堆栈处理有较大的开销。当数据接近或者大于堆栈空间的时候,就会出现堆栈溢出错误如果是使用DOPRA平台,可以在v_configkernel.h文件中找到默认的栈大小#defineVOS_DEFAULT_STACK_SIZE0x200000
此案例发生在广东移动业务中,数据量达5000千万用户,影响性能是秒级的,在呼叫业务中用户是不可接受的。
2.4混淆“=”与“==”
如下例子的循环判断条件中,将”==”误写成”=”,导致死循环。
Boolresult=True;
while(result=True)
result=execFunc();
wait();
while(True==result)
编程规范要求将常量写在等号左边(如“True==result”),确保编译时即可发现错误。
2.5指针使用前必须做非空检查判断
CAppService*tmp_pAppService
=dynamic_cast(m_pAppSession->getAppServiceBase());
m_pLogger=tmp_pAppService->getLogger();//tmp_pAppService指针有可能为空,那么就会core掉
CAppService*tmp_pAppService=NULL;
tmp_pAppService=dynamic_cast(m_pAppSession->getAppServiceBase());
if(tmp_pAppService!
=NULL)
m_pLogger=tmp_pAppService->getLogger();
else
错误处理;
类似数据库查询对结果集读取也应该先检查再使用
如:
ENIP:
:
IMemRecordSet*tmp_pRecordSet=t_dataSetRef.getRecordSet();
if(NULL==tmp_pRecordSet)
ELOG("RecordSetpointerisNULL.");
t_bHasRecord=ENIP:
False;
elseif(tmp_pRecordSet->eof())
LOG("NorecordfoundinLostCallServiceBlackList.");
在函数或方法中涉及指针或引用传递须遵循以下规则:
1.函数属于私有类型,如果内部不做检查,外部调用时必须做检查(函数内部需要加以说明);如果内部做了检查,外部调用时可以不做检查(调用时需要加以说明);
2.函数属于公有类型,函数内部必须做检查,在外部调用时可以不做检查(调用时需要加以说明);
2.6宏定义未充分封装替换参数
#defineRECT_SIZE(X*Y)
X=length+1;
Y=width+1;
最终结果为length+width+1,与预计不符合
#defineRECT_SIZE((X)*(Y))
2.7case语句遗漏break或缺少default分支条件
caseWM_CLOSE:
Close();//遗漏break语句,执行完关闭后,重新使用已释放资源
caseWM_READ:
Read();
break;
switch()
Close();
…
default:
//不要遗漏default语句
2.8未判断函数返回值
//处理计费矩阵查询结果
doMatrixAnalysis(request,response)
以上代码忽略了函数的返回值检查,调用计费矩阵分析方法,函数原型如下:
intdoMatrixAnalysis(constTiChargeMatrixAnalysisReqArg&reqArg,TiChargeMatrixAnalysisResArg&resArg);
如此,有可能在后续计费执行过程出现严重错误……
if(success==doMatrixAnalysis(request,response))
….//计费矩阵分析成功
ELOG("doMatrixAnalysisfailureandreleasethecall");
t_bIsError=ENIP:
True;//计费矩阵分析失败,异常退出
2.9判断无符号数是否小于0
unsignedcharc;
//c赋值为具体的循环次数,会导致死循环
while(c-->=0)
//dosomething…
3【1级错误】
3.1使用野指针
(1)使用未分配空间的指针
voidfunc()
char*p;
if(NULL!
=p)
printf(“%s”,p);
(2)内存空间释放后指针未置Null,内存指针仍被继续使用
void*g_pBuf=NULL;
voidATM_CellRecv(U8*pBuf,U32ulLen)
g_pBuf=pBuf;//g_pBuf通过pBuf赋值指向内存区域
......
=pBuf)
free(pBuf);//只是释放了内存,而g_pBuf并没有置成NULL.
voidfunc2()
g_pBuf=pBuf;
=g_pBuf)
//赋值操作,将导致非法内存改写
3.2内存拷贝忽略字符串结尾标志’\0’
voidfun()
chardest[10];
charsrc[]="0123456789";
memcpy(dest,src,sizeof(src));
intiLenght=sizeof(src);
char*dest=newchar[iLenght];
memcpy(dest,src,iLenght);
deletepdest;
字符串结束符相关有许多安全函数可供调用:
snprintf,strncpy,strncat,safecopy等等,不允许使用非安全函数:
sprint,strcpy
3.3分支流程未释放动态申请的内存
voidFunction1(intnSize)
char*p=(char*)malloc(nSize);
if(!
GetStringFrom(p,nSize))
MessageBox(“Error”);
return;
…//usingthestringpointedbyp;
free(p);
当函数GetStringFrom()返回零的时候,指针p指向的内存就不会被释放。
这是一种常见的发生内存泄漏的情形。
程序在入口处分配内存,在出口处释放内存,但是c函数可以在任何地方退出,所以一旦有某个出口处没有释放应该释放的内存,就会发生内存泄漏。
内存分配方式有三种:
(1)从静态存储区域分配。
内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。
例如全局变量,static变量,静态内存区。
(2)在栈上创建。
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。
程序在运行的时候用malloc申请任意多少的内存,程序员自己负责在何时用free释放内存。
动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
一般我们常说的内存泄漏是指堆内存的泄漏。
堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。
应用程序一般使用malloc,realloc等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
3.4数据类型不一致,变量或参数赋值出错
shortintx;
inty;
void*p ;
p=&x;
*(int*)p=y;
x只有两个字节的空间,而int需要4个字节的空间,实际上己出问题,编译器未设定字节对齐时会出错,在linux下不会出现入参被踩。
还有一个例子,会导致入参被踩:
intfunc(shortints,int*pi)
…//dosomething
*pi=0;
return0;
intmain(intargc,char*argv[])
shortinty;
intiRet;
x=1;
iRet=func(x,(int*)&y);
调用函数func后,x=0。
3.5循环体内改写循环变量
unsignedlongi;
for(i=0;i<1024;c++)
for(i=0;i<512;c++)//尽量避免在循环中修改循环变量
//dosomething
3.6魔鬼数字
0x01,“|”,“”,“B-DISP”;
if("B-DISP"==attributeList[i].attributeType)
……..
将字符串"B-DISP"在*.h文件中定位为字符串常量ECC_LDAPATTRS_B-DISP
constTStringECC_LDAPATTRS_B-DISP="B-DISP";
if(ECC_LDAPATTRS_B-DISP==attributeList[i].attributeType)
3.7对只读参数未加const修饰
voidFunc(char*src,char*dst,intlen)
strncpy(src,dst,len);//误操作将src的值改写
voidFunc(constchar*src,char*dst,intlen)
strncpy(dst,src,len);
3.8各种资源句柄没有释放
FILE*fp1=fopen(“file”,w+);
....
Return0;
首次打开文件,使用后未关闭文件句柄,执行不成功。
FILE*fp2=fopen(“file”,a+);
=fp2)
fclose(fp2);
Else
Return-1;
CString中GetBuffer和ReleaseBuffer的使用;
信号量、临界区和互斥锁等同步对象资源;
线程进程资源;
3.9变量名、枚举名、常量名字面意思、注释含义与使用时相反
CVACSubScirbeReq中字段:
CMsgFieldIntm_isNeedNotifySP;//CRM侧订购的是否需要通知SP
该字段本意是个枚举值(ENUM),取值有2个:
enumisNotifySP
Notify_SP_NO=1,//不通知
Notify_SP_YES=2//通知SP
};
//下面的逻辑是需要通知sp才处理的,枚举含义和实际意义完全颠倒
if(From_CRM==pSessionInfo->m_reqInfo.m_CRMOrderFlag.asInt()
&&Notify_SP_NO==pSessionInfo->m_reqInfo.m_isNeedNotifySP.asInt())
3.10所有的可能抛异常的地方都要有捕获
constchar*sql="selectmsisdn,serviceid,spid,billmonth,amount,account,rewardAmount,rewardAccount,realaccountfromhistoryrecordwheremsisdn=:
v1andserviceid=:
v2andspid=:
v3";
TINTnCommandIndex=m_pDbAgent->excute(sql,msisdn,pa,paCount);
if(nCommandIndex==Failed)
returnSearchResult_DBError;
CRecordSetrs;
rs.setSACommand(&CDBAgent:
getInstance()->s_commandSet[nCommandIndex]);
//完全没有try和catch保护,一旦数据库出错就是一个core
4【2级错误】
4.1循环变量数据类型太小
unsignedshorts;
//s>255
for(c=0;c
4.2函数局部变量或参数过大,堆栈溢出
voidFunction(void){
charb[0x200000];
intmain()
ret=function();
函数参数的输入、输出如果是一个比较大的结构,要用指针带入保存输入数据的内存地址,用指针带入保存输出的内存地址,不要直接用值传递的方式输入和输出。
如果函数的较大的输入输出使用值传递方式,对函数堆栈处理有较大的开销。
当数据接近或者大于堆栈空间的时候,就会出现堆栈溢出错误
如果是使用DOPRA平台,可以在v_configkernel.h文件中找到默认的栈大小
#defineVOS_DEFAULT_STACK_SIZE0x200000
copyright@ 2008-2023 冰点文库 网站版权所有
经营许可证编号:鄂ICP备19020893号-2