C++低级错误案例整理V1x.docx
- 文档编号:18047981
- 上传时间:2023-08-07
- 格式:DOCX
- 页数:15
- 大小:21.02KB
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)数组下标是循环变量
voidmain()
{
inti,max,b[10];
getMax(&max);
for(i=0;i { 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_pLogger=tmp_pAppService->getLogger();//tmp_pAppService指针有可能为空,那么就会core掉 正确: CAppService*tmp_pAppService=NULL; tmp_pAppService=dynamic_cast 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判断无符号数是否小于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赋值指向内存区域 ...... 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,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[]) { shortintx; shortinty; intiRet; …//dosomething 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+); ...... 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保护,一旦数据库出错就是一个core 4【2级错误】 4.1循环变量数据类型太小 unsignedcharc; unsignedshorts; //dosomething //s>255 for(c=0;c { //dosomething } 4.2函数局部变量或参数过大,堆栈溢出 voidFunction(void){ charb[0x200000]; } intmain() { ret=function(); } 函数参数的输入、输出如果是一个比较大的结构,要用指针带入保存输入数据的内存地址,用指针带入保存输出的内存地址,不要直接用值传递的方式输入和输出。 如果函数的较大的输入输出使用值传递方式,对函数堆栈处理有较大的开销。 当数据接近或者大于堆栈空间的时候,就会出现堆栈溢出错误 如果是使用DOPRA平台,可以在v_configkernel.h文件中找到默认的栈大小 #defineVOS_DEFAULT_STACK_SIZE0x200000
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 低级 错误 案例 整理 V1x