c语言软件编程规范培训案例.docx

上传人:b****7 文档编号:11214073 上传时间:2023-02-25 格式:DOCX 页数:65 大小:137.87KB
下载 相关 举报
c语言软件编程规范培训案例.docx_第1页
第1页 / 共65页
c语言软件编程规范培训案例.docx_第2页
第2页 / 共65页
c语言软件编程规范培训案例.docx_第3页
第3页 / 共65页
c语言软件编程规范培训案例.docx_第4页
第4页 / 共65页
c语言软件编程规范培训案例.docx_第5页
第5页 / 共65页
点击查看更多>>
下载资源
资源描述

c语言软件编程规范培训案例.docx

《c语言软件编程规范培训案例.docx》由会员分享,可在线阅读,更多相关《c语言软件编程规范培训案例.docx(65页珍藏版)》请在冰豆网上搜索。

c语言软件编程规范培训案例.docx

c语言软件编程规范培训案例

软件编程规范培训

实例与练习

 

第一版

 

说明

 

本文分为两部分,第一部分为中研《关于规范开发人员设计编码行为、提高软件质量的通知》文件,其中包含来自测试人员总结的大量的包括逻辑类、接口类、维护类和可测试类四个方面的生动实例,是典型的软件编程规范培训实例,亦可供我司员工自学;第二部分是一个练习,作为软件编程规范教学使用。

 

 

案例与练习

第一部分

目录

一、逻辑类代码问题

第5页

1、变量/指针在使用前就必须初始化

第5页

【案例1.1.1】

第5页

2、防止指针/数组操作越界

第5页

【案例1.2.1】

第5页

【案例1.2.2】

第6页

【案例1.2.3】

第7页

【案例1.2.4】

第8页

3、避免指针的非法引用

第9页

【案例1.3.1】

第9页

4、变量类型定义错误

第10页

【案例1.4.1】

第10页

5、正确使用逻辑与&&、屏蔽&操作符

第17页

【案例1.5.1】

第17页

6、注意数据类型的匹配

第18页

【案例1.6.1】

第18页

【案例1.6.2】

第18页

7、用于控制条件转移的表达式及取值范围是否书写正确

第20页

【案例1.7.1】

第20页

【案例1.7.2】

第21页

【案例1.7.3】

第22页

8、条件分支处理是否有遗漏

第24页

【案例1.8.1】

第24页

9、引用已释放的资源

第26页

【案例1.9.1】

第26页

10、分配资源是否已正确释放

第28页

【案例1.10.1】

第28页

【案例1.10.2】

第29页

【案例1.10.3】

第30页

【案例1.10.4】

第32页

【案例1.10.5】

第33页

【案例1.10.6】

第35页

【案例1.10.7】

第38页

11、防止资源的重复释放

第39页

【案例1.11.1】

第39页

12、公共资源的互斥性和竞用性

第40页

【案例1.12.1】

第40页

【案例1.12.2】

第40页

二、接口类代码问题

第43页

1、对函数参数进行有效性检查

第43页

【案例2.1.1】

第43页

【案例2.1.2】

第43页

【案例2.1.3】

第44页

【案例2.1.4】

第46页

【案例2.1.5】

第47页

【案例2.1.6】

第48页

2、注意多出口函数的处理

第49页

【案例2.2.1】

第49页

三、维护类代码问题

第51页

1、统一枚举类型的使用

第51页

【案例3.1.1】

第51页

2、注释量至少占代码总量的20%

第51页

【案例3.2.1】对XXX产品BAM某版本部分代码注释量的统计

第51页

四、产品兼容性问题

第52页

1、系统配置、命令方式

第52页

【案例4.1.1】

第52页

【案例4.1.2】

第53页

2、设备对接

第54页

【案例4.2.1】

第54页

3、其他

第55页

【案例4.3.1】

第55页

五、版本控制问题

第58页

1、新老代码中同一全局变量不一致

第58页

【案例5.1.1】

第58页

六、可测试性代码问题

第59页

1、调试信息/打印信息的正确性

第59页

【案例6.1.1】

第59页

一、逻辑类代码问题

1、变量/指针在使用前就必须初始化

【案例1.1.1】

C语言中最大的特色就是指针。

指针的使用具有很强的技巧性和灵活性,但同时也带来了很大的危险性。

在XXX的代码中有如下一端对指针的灵活使用:

......

_UC*puc_card_config_tab;

......

Get_Config_Table(AMP_CPM_CARD_CONFIG_TABLE,

&ul_card_config_num,

&puc_card_config_tab,

use_which_data_area

);

......

b_middle_data_ok=generate_trans_middle_data_from_original_data(

puc_card_config_tab,

Ul_card_config_num)

.......

其中红色部分巧妙的利用指向指针的指针为指针puc_card_config_tab赋值,而在兰色部分使用该指针。

但在Get_Config_Table函数中有可能失败返回而不给该指针赋值。

因此,以后使用的可能是一个非法指针。

指针的使用是非常灵活的,同时也存在危险性,必须小心使用。

指针使用的危险性举世共知。

在新的编程思想中,指针基本上被禁止使用(JAVA中就是这样),至少也是被限制使用。

而在我们交换机的程序中大量使用指针,并且有增无减。

2、防止指针/数组操作越界

【案例1.2.1】

1在香港项目测试中,发现ISDN话机拨新业务号码时,若一位一位的拨至18位,不会有问题。

但若先拨完号码再成组发送,会导致MPU死机。

处理过程:

查错过程很简单,按呼叫处理的过程检查代码,发现某一处的判断有误,本应为小于18的判断,写成了小于等于18。

结论:

代码编写有误。

思考与启示:

1、极限测试必须注意,测试前应对某项设计的极限做好充分测试规划。

2、测试极限时还要注意多种业务接入点,本例为ISDN。

对于交换机来说,任何一种业务都要分别在模拟话机、ISDN话机、V5话机、多种形式的话务台上做测试。

对于中继的业务,则要充分考虑各种信令:

TUP、ISUP、PRA、NO1、V5等等。

【案例1.2.2】

对某交换类进行计费测试,字冠011对应1号路由、1号子路由,有4个中继群11,12,13,14(都属于1#模块),前后两个群分别构成自环。

其中11,13群向为出中继,12,14群向为入中继,对这四个群分别进行计费设置,对出入中继都计费。

电话60640001拨打01160010001两次,使四个群都有机会被计费,取话单后浏览话单发现对11群计费计次表话单出中继群号不正确,其它群的计次表中出中继群号正常。

处理过程:

与开发人员在测试组环境多次重复以上步骤,发现11群的计次表话单有时正常,有时其出中继群号就为一个随机值,发生异常的频率比较高。

为什么其它群的话单正常,唯独11群不正常呢?

11群是四个群中最小的群,其中继计次表位于缓冲区的首位,打完电话后查询内存发现出中继群号在内存中是正确的,取完话单后再查就不正确了。

结论:

话单池的一个备份指针Pool_head_1和中继计次表的头指针重合,影响到第一个中继计次表的计费。

思考与启示:

随机值的背后往往隐藏着指针问题,两块内存缓冲区的交界处比较容易出现问题,在编程时是应该注意的地方。

【案例1.2.3】

【正文】

在接入网产品A测试中,在内存数据库正常的情况下的各种数据库方面的操作都是正常的。

为了进行数据库异常测试,于是将数据库内容人为地破坏了。

发现在对数据库进行比较操作时,出现程序跑死了现象。

经过跟踪调试发现问题出现在如下一段代码中:

1for(i=0;idbf_count;i++)

2{

3pDBFat=(_NM_DBFAT_STRUC*)(NVDB_BASE+DBFAT_OFFSET+i*DBFAT_LEN);

4if(fat_check(pDBFat)!

=0)

5{

6pSysHead->system_flag=0;

7head_sum();

8continue;

9}

10if(strlen(dbf->dbf_name)!

=0&&strncmp(dbf->dbf_name,pDBFat->dbf_name,strlen(dbf->dbf_name))==0)

11{

12dbf_ptr1=(_UC*)pDBFat->dbf_head;

13filesize=pDBFat->dbf_fsize;

14break;

15}

16}

在测试时发现程序死在循环之中,得到的错误记录是"BusError"(总线出错),由此可以说明出现了内存操作异常。

经过跟踪变量值发现循环变量i的阀值pSysHead->dbf_count的数值为0xFFFFFFFF,该值是从被破坏的内存数据库中获取的,正常情况下该值小于127。

而pDBFat是数据库的起始地址,如果pSysHead->dbf_count值异常过大,将导致pDBFat值超过最大内存地址值,随后进行的内存操作将导致内存操作越界错误,因而在测试过程中数据库破坏后就出现了主机死机的现象。

上面的问题解决起来很容易,只需在第一行代码中增加一个判断条件即可,如下:

 for(i=0;idbf_coun&&i

//MAX_DB_NUM=127

这样就保证了循环变量i的值在正常范围内,从而避免了对指针pDBFat进行内存越界的操作。

  从上面的测试过程中,我们可以看到:

如此严重的问题,仅仅是一个简单的错误引起的。

实际上,系统的不稳定往往是由这些看似很简单的小错误导致的。

这个问题给我们教训的是:

在直接对内存地址进行操作时,一定要保证其值的合法性,否则容易引起内存操作越界,给系统的稳定性带来潜在的威胁。

【案例1.2.4】

近日在CDB并行测试中发现一个问题:

我们需要的小区负荷话统结果总是为零,开始还以为小区负荷太小,于是加大短消息下发数量,但还为零,于是在程序中加入测试代码,把收到的数据在BAM上打印出来,

结果打印出来的数据正常,不可能为零,仔细查看相关代码,问题只可能在指针移位上有问题,果然在函数中发现一处比较隐蔽的错误。

/*功能:

一个BM模块内所有小区CDB侧广播消息忙闲情况*/

/*************************************************************/

voidCell_CBCH_Load_Static(structMsgCBFAR*pMsg)

{

memcpy((_UC*)&tmp_msg,pMsg,sizeof(tmp_msg));

pMsg=pMsg+sizeof(tmp_msg);//sizeof(tmp_msg)=10;本意是想移动10个字节,可是实际上指针移动了10*sizeof(structMsgCB)个字节;

CellNum=tmp_msg.usCellNum;

}

所以结构指针传入函数后,如要进行指针移动操作,最好先将其转化为_UC型再说。

总之指针操作要小心为上。

3、避免指针的非法引用

【案例1.3.1】

【正文】

在一次测试中,并没有记得做了什么操作,发现HONET系统的主机复位了,之后,系统又工作正常了。

由于没有打开后台的跟踪窗口,当时查了半天没有眉目。

过了半天,现象又出现了,而且这次是主机在反复复位,系统根本无法正常工作了。

我凭记忆,判断应该是与当时正在测试的DSL板的端口配置有关。

于是将板上所有端口配置为普通2B+D端口,重新加载在主机数据,现象消失。

于是初步定位为主机在DSL端口处理过程中有重大错误。

我在新的数据上努力恢复原出问题的现象,却一直没有重现,于是恢复原数据,加载后立即重现。

并注意到,当DSL端口激活时,主机复位。

仔细比较两种数据的差别,发现出现主机复位问题的数据中DSL板配置了MNT/MLT端口,但是没有做DSL端口之间的半永久数据。

于是在程序中不断加打印语句,通过后台的DBWIN调试程序跟踪,最后终于定位为:

每当执行到portdsl.c的DeviceDslMsgProc()函数中处理U口透传的

if(SPC_STATE_OK==pSpcCB->bySpcState)

语句时,主机复位。

但是该语句似乎并无不妥。

再分析整个函数,pSpcCB在函数前部分已经被赋值,

pSpcCB=SpcCB+(PortTable+index)->spcNo;

但由于得到index后,没有任何判断,导致若MNT/MLT端口没有做半永久,端口激活后,执行此部分函数,(PortTable+index)->spcNo有可能为NULL_WORD,于是,运算后,pSpcCB可能为非法值。

此时主机在取进行判断,就不知会导致什么后果了。

其实,改起来很简单,只要在这两句前增加一个判断就行了。

于是,修改代码为:

if((PortTable+index)->spcNo!

=NULL_WORD)

{

pSpcCB=SpcCB+(PortTable+index)->spcNo;

if(SPC_STATE_OK==pSpcCB->bySpcState)

{。

}

}

修改后,问题不再重现。

经过分析可以发现,编译环境是有很大的容许空间的,若主机没有做充分的保护,很可能会有极严重的随即故障出现。

所以编程时一定要考虑各种可能情况;而测试中遇到此类死机问题,则要耐心的定位到具体是执行哪句代码时出现的,再进行分析。

因为问题很隐蔽,直接分析海一样的代码是很难发现的。

4、变量类型定义错误

【案例1.4.1】

【正文】

在FRI板上建几条FRPVC,其DLCI类型分别为:

10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。

相应的DLCI值为:

16、234、991、126975、1234567,然后保存,重起MUX,观察PVC的恢复情况,结果DLCI值为16、234和991的PVC正确恢复,而DLCI=126975的PVC恢复的数据错误为61439,而DLCI=1234567的PVC完全没有恢复。

对于17/4类型,DLCI=126975的PVC在恢复时变成61439,根据这条线索,查找原因,发现126975-61439=65535,转化二进制就是10000000000000000,也就是说在数据恢复或保存时把原数据的第一个1给忽略了。

此时第一个想法是:

在程序处理中,把无符号长整型变量当作短整型变量处理了,为了证实这个判断,针对17bit/4bytes类型又重新设计测试用例:

(1)先建PVC,DLCI=65535,然后保存,重起MUX,观察PVC的恢复情况,发现PVC能够正确恢复;

(2)再建PVC,DLCI=65536,然后保存,重起MUX,观察PVC的恢复情况,此时PVC不能正确恢复。

至此基本可以断定原因就是出在这里。

带着这个目的查看原代码,发现在以下代码中有问题:

int_GetFrDlci(DWORD*dwDlci,char*str,DWORDdwDlciType,DWORDdwPortType,DWORDdwSlotID,DWORDdwPortID)

{DWORDtempDlci;

charszArg[80];

1charszLine[80];

IDLowPVCEP;

DWORDdwDlciVal[5][2]=

{{16,1007},{16,1007},{1024,64511},

{2048,129023},{131072,4194303}};

...

}

typedefstructtagFrPppIntIWF

{

...

WORDwHdlcPort;

WORDwHdlcDlci;

WORDwPeerHdlcDlci;

WORDwPeerOldAtmPort;

...

}SFrPppIntIWFData;

DWORDSaveFrNetIntIWFData(DWORD*pdwWritePoint)

{

BYTEbSlotID,bPeerSlotID;

DWORDdwCCID,dwPeerCCID;

WORDwHdlcPort,wAtmPort,wIci,wPeerIci,wPeerHdlcPort;

WORDwCount;

...

}

DWORDSaveFrNetExtIWFData(DWORD*pdwWritePoint)

{

BYTEbSlotID;

DWORDdwCCID,dwPeerCCID;

WORDwHdlcPort,wAtmPort,wIci;

WORDwCount;

...

unSevData.FrNetExtIWF[wCount].bSlotID=bSlotID;

unSevData.FrNetExtIWF[wCount].wHdlcPort=wHdlcPort;

unSevData.FrNetExtIWF[wCount].wHdlcDlci=gFrPVCEP[bSlotID][gFrPVCC[bSlotID][dwCCID].dwLoPVCEP].dwDLCI;

unSevData.FrNetExtIWF[wCount].wOldAtmPort=wAtmPort;

unSevData.FrNetExtIWF[wCount].wAtmDlci=gFrPVCEP[bSlotID][gFrPVCC[bSlotID][dwCCID].dwHiPVCEP].dwDLCI;

unSevData.FrNetExtIWF[wCount].dwMapMode=gFrPVCC[bSlotID][dwCCID].dwMapMode;

     ...

}

DWORDRestoreFrNetExtIWFData(WORDwSlotID,BYTE*pReadPoint)

{

WORDwCount,wTotalNetIWF;

BYTEbSlotID,bHdlcDlciType,bAtmDlciType;

WORDwOldAtmPort,wAtmDlci,wHdlcPort,wHdlcDlci;

DWORDdwMapMode,dwCIR,dwBe;

DWORDdwCCID,dwResult,dwAtmPort;

wTotalNetIWF=g_MuxData.SevDataSize.wFrNetExtIWFNum;

...

}

DWORDRestoreFrHdlcIntIWFData(WORDwSlotID,BYTE*pReadPoint)

{

WORDwCount,wTotalHdlcIWF;

DWORDdwCCID,dwPeerCCID,dwAtmPort,dwPeerAtmPort;

DWORDdwResult;

BYTEbSlotID,bPeerSlotID;

WORDwHdlcPort,wOldAtmPort,wCIR;

WORDwPeerHdlcPort,wPeerOldAtmPort;

...

}

其中涉及DLCI值的变量都为WORD(即无符号短整型)类型,在程序的处理时,出现WORD和DWORD(无符号长整型)类型在一句中同时存在的情况,至此可以判断问题出在这里。

由于DLCI值在不同类型时的取值范围不同,前三种类型的取值范围为16~991,第四种取值范围为2048~126975,第五种取值范围为131072~4194303,所以当采用前三种DLCI类型时,采用WORD类型最大值为65535,已经完全够用了;而对于第四种类型时,其取值在超过65535时,获取DLCI值的函数_GetFrDlci()采用DWORD类型,而负责保存和恢复的两个函数SaveFrNetExtIWFData()和RestoreFrNetExtIWFData(),都把DLCI的值当作WORD类型进行处理,因此导致DLCI取值越界,于是程序把原本为长整型的DLCI强制转换成整型,从而导致DLCI值在恢复时,比原数据小65536。

而在程序运行过程中,这些数据保存在DRAM中,程序运行直接从DRAM中获取数据,程序不会出错;当FRI板复位或插拔后,需要从FLASH中读取数据,此时恢复函数的错误就表现出来。

另一个问题是为什么23/4类型的DLCI数据不能恢复?

这是由于对于23/4类型的PVC,其DLCI的取值范围为:

131072~4194303,而程序强制转换并恢复的数据最大只能是65535,所以这条PVC不能恢复。

至此,DLCI数据恢复出错的原因完全找到,解决的方法是将DLCI的类型改为DWORD类型。

从这个案例可以看出,在程序开发中一个很低级的错误,将在实际工作中造成很严重的后果。

【案例1.4.2】

【正文】

在FRI板上建几条FRPVC,其DLCI类型分别为:

10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。

相应的DLCI值为:

16、234、991、126975、1234567,然后保存,重起MUX,观察PVC的恢复情况,结果DLCI值为16、234和991的PVC正确恢复,而DLCI=126975的PVC恢复的数据错误为61439,而DLCI=1234567的PVC完全没有恢复。

对于17/4类型,DLCI=126975的PVC在恢复时变成61439,根据这条线索,查找原因,发现126975-61439=65535,转化二进制就是10000000000000000,也就是说在数据恢复或保存时把原数据的第一个1给忽略了。

此时第一个想法是:

在程序处理中,把无符号长整型变量当作短整型变量处理了,为了证实这个判断,针对17bit/4bytes类型又重新设计测试用例:

(1)先建PVC,DLCI=65535,然后保存,重起MUX,观察PVC的恢复情况,发现PVC能够正确恢复;

(2)再建PVC,DLCI=65536,然后保存,重起MUX,观察PVC的恢复情况,此时PVC不能正确恢复。

至此基本可以断定原因就是出在这里。

带着这个目的查看原代码,发现在以下代码中有问题:

int_GetFrDlci(DWORD*dwDlci,char*str,DWORDdwDlciType,DWORDdwPortType,DWORDdwSlotID,DWORDdwPortID)

{DWORDtempDlci;

charszArg[80];

charszLine[80];

IDLowPVCEP;

DWORDdwDlciVal[5][2]=

{{16,1007},{16,1007},{1024,64511},

{2048,129023},{131072,4194303}};

...

}

typedefstructtagFrPppIntIWF

{

...

WORDwHdlcPort;

WORDwHdlcDlci;

WORDwPeerHdlcDlci;

WORDwPeerOldAtmPort;

...

}SFrPppIntIWFData;

DWORDSaveFrNetIntIWFData(DWORD

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1