《软件编程低级错误:空指针.ppt》由会员分享,可在线阅读,更多相关《软件编程低级错误:空指针.ppt(16页珍藏版)》请在三一办公上搜索。
1、2023/9/29,PSST系统工程部 C语言软件编程规范工作组,公司常见软件编程低级错误:空指针,前言,这套材料作为编程规范的辅助材料,帮助大家理解编程规范背后的原理。C和C+语言是我司的主流编程语言,然而C/C+具有很多强大的语言特性,从而导致C/C+非常复杂,使得代码更容易出现BUG、难以阅读和维护。业界知名的编程规范都对C/C+容易出现问题的语言特性进行管理。例如MISRA(汽车工业软件可靠性联合会)制定的1998版的MISRAC规范指出,一些在C看来可以接受,却存在隐患的地方有127处之多。2004版的MISRAC规范将针对C语言的规则增加到了141条。对于程序员来说,能工作的代码并
2、不等于“好”代码。“好”代码的指标很多,包括可读性、可维护性、可移植性和可靠性等。出现网上问题的代码,大多数是不良编程习惯引起的。不遵守编程规范的代码,往往也是最不可靠的代码。本胶片收集了常见的空指针案例,给出了相应的纠正措施。,申请内存没有捕获异常,【问题描述】Web服务器返回500错误。【问题定位】New内存失败,不会返回NULL,而会抛出异常,但代码中没有捕获这个异常【根本原因】C+语言中New内存失败后可以出现两种情况,一种是返回NULL,一种是抛出异常。可以在调用New函数时使用参数指定,也可以在编译选项中统一选择一种处理方式。项目组没有明确规定采用哪种处理方式,开发人员根据以前项目
3、组的经验调用New函数后,判断是否非空,但在这个项目中即使New失败也不会返回NULL。【举一反三】项目组必须规定统一的编程规范。维护老代码时,注意弄清楚老代码的设计和编程的规则。,没有判断全局指针非空,【问题描述】当主控板起来但接口板还没有起来时,使用命令undo rule-map删除所有的Rule-map,出现DA异常。【问题定位】在删除Rule-map时,如果它引用了Link-group,则将Link-group中的pLink的引用计数减一。开发人员认为全局变量pLink永远不可能为NULL,所以没有判断其合法性。正常情况下,pLink确实不可能为NULL,但当系统重启时,先恢复了Rul
4、e-map,在恢复Link-group之前,pLink指针为NULL。此时如果从命令行删除所有的Rule-map,就会出现DA异常。【纠正措施】访问pLink之前,判断其是否为NULL【根本原因】多个模块相互配合时,可能出现千奇百怪的调用顺序,开发人员认为永远不可能为NULL的参数,实际网络中却很可能发生。【举一反三】外部接口传入的参数,或其它模块维护的全局变量,使用之前必须判断是否合法,没有判断外部传入指针非空,【问题描述】终端向服务器注册,服务器返回403失败响应,这时终端向服务器发送群组广告,服务core。【问题定位】从core文件中看,是空指针引起。根据core文件中提供的的信息,反向
5、检视代码,发现蓝色部分存在问题,对获取到的变量未做空指针判断:pSession-SetUserContext(pLogic-GetPoCPCUUserContext();在用户未注册上时,服务器清空了资源,使得pLogic-GetPoCPCUUserContext()取出来时为空,但传入方与接收方都未做空指针判断,导致出现问题.【纠正措施】对传入的指针应做合法性检查IPoCPCUUserContext*pContext=pLogic-GetPoCPCUUserContext();If(NULL=pContext)pSession-SetUserContext(pContext);【举一反三】外
6、部接口传入的指针,多个对象使用,很可能一方更改了,而另一方不知道的情况,使用之前必须判断是否合法,没有判断文件输入数据非空,【问题描述】MM代码中使用了非法指针导致(空指针)服务器发生宕机。【问题定位】从操作日志分析到MM分析list.xml时出现宕机,hr=pRootNode-get_firstChild(【举一反三】外部文件读入的指针,可能为空,使用之前必须判断是否合法,没有判断文件输入数据非空(续),【问题描述】在某省项目中,现场开发了与第三方的网管系统(以后简称E1)的接口程序(以后简称E2),用于接收网管上的所有实时告警信息。在不定期的情况下,接口程序E2会core dump。【问题
7、定位】从操作日志分析到读取第三方空日志文件时宕机 FILE*fp=fopen(./writeAlarmLastTime.log,r);char buff128=;fscanf(fp,“%s”,buff);/读取最新的告警时间;由于文件writeAlarmLastTime.log为空,导致buff为空fclose(fp);long fileTime=getAlarmTime(buff);/解析获取最新的告警时间;getAlarmTime函数未检查buff指针【纠正措施】在函数getAlarmTime检查指针的合法性或者检查fscanf的返回值:如果返回值为EOF,则返回if(EOF=fscanf
8、(fp,“%s”,buff)/读取最新的告警时间 return;【举一反三】外部文件读入的指针,可能为空,使用之前必须判断是否合法,定时器处理函数没有判断指针非空,【问题描述】测试人员在测试过程中发现scf出现core:查看日志分析scf在收到CCA(Term)消息时调用函数clearDiamCCATimer清除等待CCA(Term)后,又进入处理定时器事件处理函数processDiamCCATimeOut,在该函数中出现core。【问题定位】在如下场景中将会出现空指针引用导致core:1)收到CCA(Term)消息,消息入队列2)定时器触发,定时器超时事件入队列3)处理CCR(Term)消息
9、,调用clearDiamCCATimer:清除定时器后,删除保存重发CCR消息的指针m_pResendDiamCCR4)处理定时器超时事件,调用processDiamCCATimeOut:从m_pResendDiamCCR中获取重发消息。TInterpretR TSCSM:processDiamCCATimeOut()。*pTmpDiamCCR=*(TDiamScpCCR*)(m_pResendDiamCCR-pMsgPara);/当出现上面的场景时,此时m_pResendDiamCCR为空,core。【纠正措施】在函数processDiamCCATimeOut头部增加m_pResendDia
10、mCCR是否为空的判断if(NULL=m_pResendDiamCCR)return;*pTmpDiamCCR=*(TDiamScpCCR*)(m_pResendDiamCCR-pMsgPara);【举一反三】注意定时器里面的指针,是否在其他任务里面被删除,指针初始化NULL,使用前没有检查,【问题描述】山西电信客服坐席全部签出【问题定位】CTIServer发起资源连接时,如果发现两个连接方都已经存在有非会场的资源连接,则处理时存在有空指针访问。void CSCP:AnalysisConnectRequest(CTI_RESOURCE_CONNECT_Msg*pMsg,CConnectNode
11、*pNode,CConference*pOcuppyConf)CConnectObject*pObject=NULL;.if(pSourceObject!=NULL if(!pDevice)/lint-e413 if(CONN_CONF_REQUEST=pObject-m_ucCurConnectContext)/使用指针时没有检查,导致使用空指针.pObject 仅仅赋初始值NULL,没有真正赋值;pclint有空指针告警,被错误关闭,指针初始化NULL,使用前没有检查(续),【纠正措施】变量的定义、初始化和使用要做到亲密无间:在首次使用之前定义和初始化变量CConnectObject*pO
12、bject=pDestObject;if(CONN_CONF_REQUEST=pObject-m_ucCurConnectContext)【举一反三】C99之前的C语言版本要求只能在作用域开始处定义变量,这种方式在C中已经过时。这一限制的严重问题在于,在作用域开始时没有足够的相关信息进行初始化。因此我们只有两种选择:要么用某个默认的空值(比如例子中的NULL)来初始化,这通常是一种浪费,还回导致错误;要么让变量未初始化,而这是很危险的。解决方案很简单:尽可能局部定义变量,在需要首次使用前定义和初始化变量。,空指针操作:多个条件一起判断(1),【问题描述】测试接口时,如果没有输入必选参数(操作类
13、型operationType),系统异常【问题定位】if(operationType!=null&operationType.equals(1)/分支操作 else if(operationType.equals(2)/分支操作 else/分支操作表面上代码对空指针和各种取值都进行了判断,但是当operationType为null时,第一行的判断为FALSE,然后执行第二个判断分支,立刻出现空指针异常。其实,我们代码中经常会出现这样的低级错误,编码者第一次判断时思路很清晰,要对对象判空,但是以后想当然认为第一次已经判空了,else if之后就不需要再次判断。【纠正措施】见下页,空指针操作:多个
14、条件一起判断(2),【纠正措施】将判空作为单独的条件,这样代码虽然多了一行,却清晰了很多if(null=operationType)/分支操作 else if(operationType.equals(1)/分支操作 else if(operationType.equals(2)/分支操作 else/分支操作【举一反三】将多条判断语句写在一起,非常容易出错。每一行代码最好只完成一个功能,空指针操作:&写成了|,【问题描述】代码飞检发现下面代码:typedef mapMsgmap;Msgmap m_mapMsgData;Msgmap:iterator itor=m_mapMsgData.find
15、(nMsgID);if(m_mapMsgData.end()!=itor)|!itor-second)【问题定位】当 m_mapMsgData.end()!=itor为非,会继续执行!itor-second,但此时itor为空指针。【纠正措施】将|修改成,空指针操作:Map取值自动添加空指针(1),【问题描述】版本验证中,进行关闭操作,引起coredump。问题无规律重现。【问题定位】分析dump文件,导致coredump的代码是:if(itRevTElink-second)-GetLinkstat()=TELINK_STATE_DISCONNECT).itRevTElink是从m_mapIn
16、dexToLink中取出的,唯一可能的原因就是m_mapIndexToLink中存在空指针。检视向map插入键值的代码:pCOSNTELink pLink=m_mapIndexToLinkpairLinkIndex(link.GetLocalNeID(),link.GetLocalTeIfID();if(NULL=pLink)return false;/判断了键值为空,但直接返回,没有将这个空键值从MAP中删除查看STL中实现map的源码,发现使用mapkey取值时,如果键值不存在,会自动插入一个节点,该节点键值为key,值则由默认构造函数自动生成。在上面代码中,自动插入了NULL值,造成后面
17、引用了空指针造成coredump。【纠正措施】见下页,空指针操作:Map取值自动添加空指针(2),【纠正措施】map取值时先用find进行查找,找到了再进行相关操作:IndexToLin:iterator itFind m_mapIndexToLinkpairLinkIndex link.GetLocalNeID(),link.GetLocalTeIfID();if(m_mapIndexToLinkpairLinkIndex.end()=iteFind)return false;/通过迭代器取出的指针要进行非空判断:if(itRevTElink-second!=NULL&(itRevTElink-second)-GetLinkstat()=TELINK_STATE_DISCONNECT).,