您现在所在的位置是:首页 > 业界新闻

服装行业软件的静态测试方法

软件质量问题已经越来越深刻地影响到了产品的质量,甚至有些时候是致命的,在服装服饰等领域更是如此。然而,很少有程序员知道什么样的程序是安全的程序。很多程序只是表面上可以干活,还存在着大量的隐患。当然,这其中也有C语言自身的原因。因为任何一种语言是入门容易。但是得道难,其灵活的编程方式和语法规则对于一个新手来说很可能会成为机关重重的陷阱。同时,高级语言的定义还并不完全,即使是国际通用的C语言标准,也还存在着很多未完全定义的地方,因此可见针对软件的测试是何等的重要,这是尽最大可能减少软件缺陷的唯一途径。以服装行业软件为例,下面介绍测试内容之一的静态测试。

静态测试是基于期望属性、专业经验、通用标准来对工作件的特征进行详细检查的一种测试方法。所谓工作件,也就是静态测试的测试对象,是不同种类的产品交付件,即一切项目过程文档,例如系统设计说明书、产品需求文档、开发设计文档(详细设计说明书、数据库设计说明书)、源代码以及测试文档。

1.静态测试的特质
(1)静态测试的查错和分析功能是其他方法所不能替代的。
(2)静态测试的目的是确保工作件中的缺陷被尽早发现和处理,尽可能在服装行业软件开发生命周期的早期阶段关闭缺陷产生的源头。
(3)静态测试人员主要是寻找三类缺陷:错误,意味着没有进行正确的改变;遗漏,意味着有些该改变的没有改变;额外,意味着非有意的改变或增加。

2.静态测试的优点

(1)静态测试有助于缓解测试执行阶段工作的压力。传统测试方法,测试部门的工作往往是前松后紧,工作分配和工作压力极不平衡。大家经常会听到测试人员反应“现在在测试准备阶段,就是写写测试用例.不忙,忙的时候还是测试执行的时候,经常要加班加点”。造成这种现象的原因就在于,测试人员还只是把测试开发完成后的“软件成品”——当做“测试工作的内容”,并不把前期的用于制造软件的“设计图纸”——需求、设计文档——当做测试对象来花时间和精力进行测试。

运用静态测试后:
1)加深对项目的理解,使测试计划和测试设计质量得到提高。
2)使得测试用例全面、有效,从“撞问题”转变为有目的的“找问题”。
3)提前了对项目的理解,减少了测试执行时的摸索时间,从而加快了测试进度。
4)提前发现问题。降低了缺陷修复成本、回归测试成本以及沟通成本,同时降低项目风险,减轻了测试执行时的压力。

(2)静态测试可有效缓解因工期和人力因素对项目的影响。目前服装行业软件项目普遍都存在:项目周期短和人力资源不足的情况。在这种情况下,往往会延长开发时间、压缩设计和测试执行的时间,以保证项目能如期完成。项目自身抵抗风险的能力下降,某些高风险的缺陷一旦在测试阶段暴露,将可能会导致设计被推翻,需求被迫变更,大量的代码重写和之前测试工作的徒劳,严重影响项目质量和项目进度,让项目陷人恶性循环。

运用静态测试后:
1)提前发现设计问题,协同开发一起做好功能设计,避免项目走弯路。
2)完善测试设计,明确描述分歧,细化处理功能,提高编码质量和测试质量。
3)一定程度地缓解项目工期压力和人力资源压力。

(3)静态测试有助于发挥测试人员的潜力。传统的测试是按照需求设计文档来验证程序的问题,但这个“唯一的”测试依据其实很多时候都靠不住,问题丛生。当测试人员养成完全依赖uC(或者PRD)的习惯后,便会缺乏主动思考、创新思考的能力。下意识地就把Uc和PRD当成测试的立足点,以此来验证软件产品的质量,这样将导致测试人员发现的问题质量低,问题深度不够,难以发现用户体验相关的缺陷,并且容易使测试人员当因某个测试问题与开发意见不一时,争辩时底气不足,使问题得过且过,丧失测试人员的地位。

运用静态测试后:
1)激发了测试人员的潜力,层层深入业务核心,从被动接受,转变为主动思考,敢于质疑设计,敢于异议架构。
2)锻炼了测试人员思考和分析的能力。
3)姿态的转变——“客户的代言人”。
(4)静态测试有助于测试准备阶段对测试人员的绩效评估。传统测试在测试准备阶段,测试经理除了通过测试用例对测试人员的工作情况进行评估外.很难有其他方法对其绩效进行了解。而测试设计和测试用例的产出相对是滞后的,这样就给测试经理提前预警带来了难度,一旦到了测试准备阶段后期才发现问题,就会让测试准备工作陷入被动的境地。

运用静态测试以后:
1)每天可以对每位测试人员按功能模块提交的静态测试问题数进行统计。
2)每周都可以为每位测试人员应提交的静态测试问题数目制定目标。
3)每周可以对每位测试人员的静态测试问题质量进行评估和总结,营造积极进取的测试团队氛围。

5.2静态测试解决的问题

静态测试包括代码审查、文档审查等。它可以由人工进行,充分发挥人的逻辑思维优势.也可以借助软件工具自动进行。

1.代码审查

代码审查技术为减少程序开发的错误已被使用多年。代码审查主要用于识别和消除安全缺陷,包括可导致掠夺性的缓冲器溢出的缺陷和其他的被认为“源代码安全性审查”的缺陷。这些审查方法在发现和消除一些问题方面是有效的,而利用现有的工具并不能侦测出这些问题。然而,源代码审查技术代表了非结构化的代码走查,很大程度上依赖于程序员的经验和韧性。任何的人工过程都易于产生错误,而使用更多的结构化方法可以保证更高的质量,从而那些潜在的安全缺陷就能被正确地识别和纠正。目前源代码审查技术已经被成功地用于从C/c++程序里寻找和移除软件缺陷。然而,这些审查技术通常是有缺点的、非结构化的,并且依赖于审查人员的韧性和知识。当然,这种人工的方法是高劳动强度的、易于产生人为的错误,可以使用自动化的工具来补充。在实际使用中,代码审查比动态测试更有效率,能快速找到缺陷,发现30%~70%的逻辑设计和编码缺陷;代码审查看到的是问题本身而非征兆。但是代码审查非常耗费时间,而且代码检查需要知识和经验的积累。代码检查应在编译和动态测试之前进行,在检查前,应准备好需求描述文档、程序设计文档、程序的源代码清单、代码编码标准和代码缺陷检查表等。

总之,把代码审查的目的可归纳为两点:第一个目的是确保要发布质量可靠的代码,换句话说,它对于开发过程中的下个阶段是否应该提高代码的质量是个严峻的考验。代码评审能非常有效地发现所有类型的错误,包括那些由于不正确的结构引起的错误,那些不适合商业进程的错误.还有那些简单的冗余错误。这就是为什么它对于代码质量来说是个有效的试金石。第二个目的是作为教学工具帮助开发人员学会何时并且如何应用技术来提高代码的质量、一致性和维护性。经过反复仔细地评估代码.开发人员有机会掌握不同的和潜在的更好的编码方法。

2.文档审查

文档审查的问题属于所有问题分类中比较浅显,但同时也是问题数量较为庞大的一类。主要有以下的切入点:

(1)诸如软件的需求文档中字段缺失、字段描述错误、错别字等等。

查找方法:多采用横向对比、纵向对比、多元对比的方法,细心、耐心地查找即可做好。当项目大或者工期紧的情况,可以不必花太多精力在此类问题上。

(2)设计错误问题——“设计出来了,但是设计错了”。此类问题需要对程序处理流程和业务处理逻辑非常清楚才能挖掘出来,具有较高的难度,测试经理和测试骨干应多关注和发现此类的问题。

查找方法:可在编写流程图、mm图和状态迁移图的时候,理清楚程序处理流程和业务处理逻辑,再结合PRD、UC和开发设计文档查找设计得不对的静态测试问题。

(3)设计不完善问题——“设计出来了,但是设计得不好”。“设计不完善”类的问题在大多数文档中是频频出现的,比如前后逻辑矛盾、表述歧义、表达不清晰、逻辑混乱、存在误解等等。此类问题应该是项目组成员重点关注的问题,因为这直接影响了了解需求的深度和广度,测试用例的编写实效,以及将来测试执行时的测试粒度和深度。

查找方法:要发现此类问题,同样可以借助编写流程图和状态迁移图,同时多思考,多分析,多询问,多考察,借助所有提交静态测试的文档,真正做到“咀嚼需求”,这样能提高测试质量和测试效果,使事半功倍。

(4)设计遗漏问题。此类问题在各项目静态测试中所占比重是较小的,但若是有,则是将是高质量的问题。此类问题需要站得高,看得远,没有多年的相关业务背景的积淀和对项目的充分广泛的涉猎,此类问题是很难发现的。

查找方法:在充分了解业务相关背景的基础上,凭借业务设计经验,充分理解了文档和设计思想后,才能发现其中没有考虑到的问题。

(5)需求问题。此类问题是以发现目前设计与原始需求相矛盾、相抵触或者不一致的问题为目的,弥补因某些原因错写、漏写的需求要点,保证设计的完整性、完善性和正确性。

文档审查虽然在整个的审查中占到很少的一部分的比例,但是对于提高文档的的质量,无疑是很必要的、也是唯一的方法。

5.3静态测试的规范

一个程序能够符合编程规范。不仅需要程序员按照规范编程,编译器也需要对所编译的代码进行规则检查。比如现在,很多编译器开发商都对MISRAC规范有了支持,比如IAR的编译器就提供了对MISRAC:1998规范127条规则的检查功能。

MISRAC:2004认为C程序设计中存在的风险可能由5个方面造成:程序员的失误、程序员对语言的误解、程序员对编译器的误解、编译器的错误和运行出错。
当然程序员的失误是司空见惯的。程序员是人,难免会犯错误。很多由程序员犯下的错误可以被编译器及时地纠正(如键入错误的变量名等),但也有很多会逃过编译器的检查。相信任何一个程序员都曾经犯过将“一一”误写成“一”的错误,编译器可能不会认为if(x—y)是一个程序员的失误。

C语言可以产生非常紧凑、高效的代码,一个原因就是c语言提供的运行错误检查功能很少,虽然运行效率得以提高,但也降低了系统的安全性。

有句话说得好,“正确的观念重于一切”。规范对于程序员来讲,一个很重要的意义就是提供给他们一些建议,让他们逐渐树立一些好的编程习惯和编程思路,慢慢摒弃那些可能存在风险的编程行为,编写出更为安全、健壮的代码。比如,很多程序员都会忽略注释的重要性,但这样的做法会降低程序的可读性,也会给将来的维护和移植带来风险。程序员经常要接触到各种的编译器,而很多C程序在不同编译器下的处理是不一样的。例如MISRAC:2004有一条强制规则,要求程序员把所有和编译器特性相关的C语言行为记录下来。这样在程序员做移植工作时,风险就降低了。

对于各种规范,下面举例进行介绍。

5.3.1数据类型相关的编程

1.数据类型转换

程序员对数据类型的转换有很清晰的认识,在必要的地方做了正确的显式强制转换,那程序就是安全的。但有时由于程序员的疏忽,或者是过于相信编译器的“智慧”程度,导致表达式中有很多隐式转换(即没有显式地强制转换),而这些隐式数据类型转换很可能就构成致命的漏洞。MISRAC中数据类型转换规则的着眼点,即是避免有漏洞的隐式数据转换。

在介绍MISRAC关于数据类型转换的部分规则之前,先介绍整型操作数的“平衡”原则。所谓整型操作数“平衡”原则,即对于隐式表达式,编译器会按照既定规则对操作数进行位数扩充,其中int和unsignedint在整型表达式“平衡”过程中占重要地位。为此,MISRAC提出了相应规则。

使用了多余的强制类型转换(MISRA89)。

对于这个规则,MISRA给出了关于整型和浮点的两方面的详细解释。

(1)关于整型的。

1)整型操作数不是被扩充为更多位数的同符号整数。

2)表达式是复杂表达式。

3)表达式不是常数表达式,是函数的参数。

4)表达式不是常数表达式,是函数的返回表达式。

(2)关于浮点数的。

1)浮点型操作数不是被扩充为更多位数的同符号浮点数。

2)表达式是复杂表达式。

3)表达式是函数的参数。

4)表达式是函数的返回表达式。

整型表达式规则和浮点数表达式规则基本类似,只是浮点数表达式规则更为苛刻一些,对浮点型的常数也作了严格的限定。这两条规则中,出现了“复杂表达式”的概念。请注意,MI—sRAC中“复杂表达式”的概念和其他介绍C编程规范书籍中“复杂表达式”的概念是不一样的。在MISRAC中,非“复杂表达式”基本只限制在常数表达式或者函数的返回值。当然作为一名优秀程序员,第一步就是以严谨的态度对待程序中的每一个数据.明白任何一个数据操作的关键,从而才能写出最清晰易懂而又安全的代码。MISRAC关于数据类型的规则可保障程序员在迈出这一步的时候不会摔倒。

2.指针的安全规范

指针赋予了C编程最大的灵活性,结构体使得C程序整齐而紧凑,联合体在某些要求注重效率的场合有精彩的表现。这三个要素是c语言的精华。然而精华并不意味着完美。C语言在赋予程序员足够的灵活性的同时,也给了程序员许多犯错的机会。所以,有必要关注指针、结构体和联合体的实现细节,从而保障程序的安全性。在此,首先介绍M1SRAC:2004中与指针相关的部分规则,接着讲解结构体和联合体的操作规范。下文凡是未加特别说明的都是强制(required)规则,个别推荐(advisory)规则则加了“推荐”的提示。

MISRAC:2004关于指针的规范主要分为三个部分:指针的类型转换规则、指针运算的规则和指针的有效性规则。

(1)指针的类型转换。指针类型转换是个高风险的操作,所以应该尽量避免进行这个操作。MISRA—C对其中可能造成严重错误的情况做了严格的限定。

(2)指针的运算。IsoC标准中,对指向数组成员的指针运算(包括算术运算,比较等)作了规范定义,除此以外的指针运算属于未定义(undifined)范围,具体实现有赖于具体编译器件,其安全性无法得到保障。MIsRAC中对指针运算的合法范围作了如下限定:以上非法指针类型转换将会丢失const或者volatile类型。丢失const属性将有可能导致在对只读内容进行写操作,编译器不会发出警告,编译器将对不具有volatitle属性的变量作优化,丢失vola—tile属性,编译器的优化可能导致程序员预先设计的硬件时序操作失效,这样的错误很难发现。关于const和volatile关键字的详细内容,读者可参考ISoC获取更多信息。

(3)指针的有效性。

这条规则的实际意义是不允许将栈对象的地址传给外部作用域的对象。对于该程序程序员希望最后的输出结果是“helloworld”这个字符串,然而实际运行时,却出现乱码(具体内容依赖于编译运行环境).简单分析一下,由于charp[]一“helloworld”这条语句是在栈中分配空间存储“helloworld”这个字符串,当函数getm()返回的时候,已分配的空间将会被释放(但内容并不会被销毁),而printf(str)涉及系统调用,有数据压栈,会修改从当前分配给数组p[]存储空间的内容,导致程序无法得到预期的结果.倘若将getm()函数体中的char“]一“helloworld”程序行改成char*q一“helloworld”。则执行main()的时候就可以正确输出“helloworld”。这是因为这样的方式,q指向的是静态数据区,而非栈中的某个单元(也就是说helloworld分配到bss区中了而不是栈中),所以,数组名是指针不假,但在实现细节上还是有很大的差异,程序员在使用指针的时候必须慎之又慎。

3.结构体、联合体的安全规范

在具体阐述为何MISRAC:2004如此排斥联合体之前,首先需要明确与联合体相关的细节:

(1)联合体的末尾有多少个填充单元。

(2)联合体的各个成员如何对齐。

(3)多字节的数据类型高低字节如何排放顺序。

(4)如果包含位字段(bit—field),各位如何排放。

由此可见,在联合体的规范中要求联合体的使用是不被允许的。以上代码在各种通信协议中使用的频率很高,接收端接收到的数据一般都以字节为单位存放,主控程序需要根据相应的协议将接受到的多个字节进行组合。

MISRA中要求所有结构体和联合体的定义必须保证完整性。

涉及ISOC中类型定义完整性等概念,碍于篇幅的原因,此处不再赘述,读者可以参阅MISRAC:2004和IsOC标准以了解更多信息,完善自己的编程风格。

5.3.2防范表达式的失控

在C语言中,表达式是最重要的组成部分之一,几乎所有的代码都由表达式构成。表达式的使用如此广泛,读者也许会产生这样的疑问,像+、一、*、/、&&这样简单的运算也会出现问题吗?程序员在编写表达式时,往往带有一些不良的习惯。即使是编写很简单的表达式,这些不良习惯也可能造成隐患,这个小小的隐患甚至可能引起整个系统的崩溃。实际上,在程序调试过程中,表达式中存在的大部分隐患皆来源于程序员的主观臆测,即认为表达式应该是按自己认为的方式执行,但结果可能完全相反。这是因为程序设计语言或编译器的某些内在机制并不如我们所想的那样。所有的编译器都遵从这一假定:程序员都是“神”,他们既了解编程语言的各种特性,也了解编译器本身一些鲜为人知的处理原则。当然,程序员不是“神”。因此,程序员在编写程序的过程中需要小心地避免编译器“设置”的各种陷阱,而问题是有些时候很难预测下一步是否会掉进一个陷阱。MISRAC规则中包含了大量关于表达式书写的规范,最大程度地防范上述可能发生的错误,告诉程序员如何编写规范的C语言表达式。这里将首先深入剖析在编译器内部表达式的解析方式,然后罗列和分析书写表达式过程中常见的不规范写法,以帮助读者避免各种不良的编程习惯。凡是未加特殊说明的都是强制(required)规则,个别推荐(Advisory)规则加了“推荐”标示。

赋值运算符为右结合,二元运算符都满足左结合;条件运算符(即问号表式)为右结合;一元运算符和后缀运算符为右结合。这意味着3p++将被解释成3(p++)而(3p)++。回到上述问题的讨论,既然二元加运算符和二元减运算符是左结合的,上述的计算果应该没有什么问题。很遗憾,C语言标准规定的只是运算符的结合顺序,而对于二元运两操作数的求值顺序则未作定义。“对于二元操作符,要先对两个操作数求值(但没有特定顺序)之后再进行运算”。因此,上述问题的答案已经揭晓:最终的结果取决于编译器特性。

如果使用的编译器总是从左向右解析表达式,则结果是:问题1的答案是result一‘2’$‘4’一oxFE;问题2的答案是result一2+2—4。如果使用的编译器总是从右向左解析表达式,则结果是:问题1的答案是result一‘4’$‘2’一0x02;问题2的答案是result一1+1—2。为了避免使用不同编译器而导致的程序结果差异,MISRAC提出了如下强制性规则:逻辑连接操作需要括号(MISRA45)。

该规则目的是确保表达式的值必须在任何求值顺序下保持一致。那么,什么时候会出现表达式的值不一致的情况呢?MISRAC列出了几种可能,如自增运算符和自减运算符使用时、函数参数传递时、函数调用时等。归纳起来,当表达式中的操作数(也可能是一个表达式)能够影响某个共享的变量,而这个共享变量又可能导致其余操作数的值发生变化时,就需要对求值顺序保持警惕。典型的一个例子就是i+(++i),此时++i的操作会引起共享变量i的变化,而i的变化将直接影响到第一个操作数的值,于是不同的求值顺序导致了不同的求值结果。在问题1中,表达式的两边都使用了uar【一GetChar()操作(确切地说是通过该函数共享了同一个数据寄存器),所以求值顺序会影响结果。

解决此类问题的最好办法是把表达式重组,即将一个较为复杂的表达式分解成若干个简单的表达式,使运算符的多个操作数之间的耦合关系得以解除,由此保证求值的顺序。例如,可以将问题1和问题2改写成如下的形式。

为了防止表达式的求值结果和所期望的结果相悖,MISRAC还提出了许多行之有效的表达式的书写规则。例如下面的出错提示:

(1)有符号操作数进行了移位操作(MISRA46)。(注:这是为了防止结果的不确定性。)

(2)移位的操作值太大(MISRA47)。(注:移位操作的右操作数就是移位的位数,比如一个8位的无符号整数,允许移位的位数范围是0~7,这是为了防止未定义的操作。)

(3)布尔表达式出现了赋值操作(MISRA121)。(注:这是为了防止误用。)

(4)无符号数赋值为负值(MISRA48)。(注:移位操作的右操作数就是移位的位数,比如一个8位的无符号整数,允许移位的位数范围是0~7,这是为了防止未定义的操作。)

在C语言编程中,大部分潜在错误来自于程序员对程序元素的误用或滥用,以及程序员对某些结构一厢情愿的理解。编写代码时,没有必要太多地去追求所谓程序的“优雅”和“风度”,程序的价值不仅在于运行,还在于为后继的可能升级和维护提供一个参考。一个“优雅”的句式和写法也许能够让作者煞费苦心并乐此不疲,但同样也会让几个月或几年后的你或其他人伤透脑筋。在硬件资源日益丰富的今天,大部分程序员已经不需要像先辈那样去考虑如何缩减一个位的存储单元。随着项目规模的扩大,对程序可读性和可维护性的要求日益突出。程序的可读性和可维护性已经成为衡量程序价值的重要标准。MISRAC为嵌入式软件的可靠性提出了中肯的建议,一些嵌入式C编译器的开发商开始把MISRA出版物作为标准并予以支持。通过学习MISRAC,可以更多地了解ANSIC中的不确定因素,并在开发中尽可能避免误入“表达式失控”的错误。

5.3.3准确的程序流控制

程序的执行流程是由条件判断、跳转和循环构成的,没有任何一个程序会缺少程序流的控制。那么像if,for,while,switch等这些程序员无比熟悉的语句也存在隐患吗?事实上,C语言是很灵活的,这种灵活性给程序员编写代码带来了很多便利,但同时也带来了很多容易导致混淆的表达。这些表达完全符合C语言标准,但有时程序员也难以发现自己犯了错误,最终的结果是使程序进入错误的执行流程。即使程序员没有犯错误.但有些容易混淆的表达也会给其他人读懂程序带来困扰,使程序的维护变得困难。除此以外,有少量控制流程的方式还会产生不确定的运行结果,而这些结果也不容易被发觉。如何使程序的流程控制清晰、准确,不产生混淆的表达呢?MISRAC给出了很多的相关规定,使程序流的控制变得规范,避免产生各种混淆和不确定性,从而最大程度上减少程序流控制中的失误,并使程序的维护更加容易。下面从几个例子出发,讲述这些混淆是如何产生的,最后给出MISRAC关于程序流控制的相关规则,帮助读者规范编程的习惯。

当然好的代码,要安全可靠、有很好的可读性和可维护性。在C语言中,一些表达方式,可能会稍微减少程序员编程的工作量,但却会使程序的流程变得难以判断,其中的错误可能就无法发现。按照MISRAC的标准来写代码,就可以避免程序流程产生混淆和混乱,排除其中的不确定因素,使程序真正按照程序员设想的工作,并使代码更清晰易懂,真正实现安全可靠,并具有良好的可读性和可维护性。

5.3.4构建安全的编译环境

预处理是编译环境处理C程序的第一个环节,但往往最先被程序员忽略。这份看似只是由编译环境做的简单工作,其实也是机关重重。通过介绍MISRAC与预处理相关的规则。希望读者能够更准确地认识编译器的预处理过程,避免出错。无论是自定义函数还是由编译环境提供的标准库函数,如果使用不当,都会存在安全隐患。能不能保证函数被正确地定义、声明和调用,关乎到整个程序的成败。这里介绍MISARC中涉及函数部分有代表性的规则,并分析制定这些规则的出发点,以帮助读者构建更为安全的编译环境。

1.函数的定义和声明

读者也许会有这样的经历:在一个头文件中定义了一个变量,又让这个头文件被多个源文件引用。这时编译器会“报怨”说重复定义了同一个函数。出错的原因与其说是粗心,不如说是一个习惯的问题。

(1)头文件中不允许包含执行代码(MISRA71)。当源文件包含某一头文件时,预处理器会将头文件的内容在包含指令处展开。显然,在头文件中函数的定义会在其他源文件中一模一样地出现,导致函数被重复定义。解决这一问题的关键是明确一个概念:所有可执行的代码或者对象和函数的定义都应在.C的源文件中,头文件中只能存在其声明。具体的做法是:为全局变量的声明增加extern修饰符,并在相应的.C源文件中定义对象或函数。

(2)不允许定义参数数量不确定的函数(MISRA19)。标准库函数printf()深受许多程序员的喜爱,因为printf()允许不确定的参数数量,用起来很方便。但是,参数数量的不确定很可能会造成编译器无法检查函数调用时的参数一致性。对于像printf()这种使用广泛的标准库函数,编译器提供了一些合适的调用机制。但程序员必须明确,编译器无法保证对用户自行定义的参数数量不确定的函数进行数据类型检查。因此,MISRAC不允许用户冒险去定义新的参数数量不确定的函数。

2.预处理

预处理是编译器处理程序的第一步。预处理会在编译器编译程序代码前做一些准备工作,最为常见的工作是处理文件包和宏定义(分别对应#include和#define两个预处理指令)。预处理器并不对源代码进行编译,只是做一些转换工作。例如将文件或宏展开等。很多程序员认为这种类似复制、粘贴的活没什么了不起,也就放松了对预处理工作的检查。其实,很多程序的失败就从这看似简单的第一步开始。宏定义是最常见的预处理指令之一。MIS—RAC关于宏定义有一些很有代表性的规则。

3.小括号

当程序中多处出现同一个数值的时候,程序员就会想起使用宏。宏定义最大的好处就是使一些常量集中起来,修改其值只需要修改一次。大大提高了程序的可维护性。编译器对宏的处理原则比较简单,宏定义只对程序文本起作用。

为了防止宏展开后因缺少括号而存在的优先级错误问题,MISRAC有如下规定:

在函数式宏定义中,任何一个参数都应加上小括号,除非是在#或##运算符中(MIs—RA72)。

通过函数的声明和定义,我们可以看到编译环境的安全,可以从编程人员的良好的变成习惯做起,越是被忽略的小问题,就有可能成为威胁安全编译环境的潜在的隐患。

文章来源: 秘奥软件网,中小企业信息化领跑者!全国咨询热线:400-9908-527_www.misall.com

最新新闻: 上一篇: 下一篇: