1、Click to edit Master title style,Click to edit Master text styles,Second level,Third level,Fourth level,Fifth level,11/7/2009,#,C语言编程规范,2,1,背景,意义,1,注释,2,标示符,3,类型,4,常量,5,声明和定义,6,初始化,7,算术类型转换,8,指针类型转换,9,表达式,2,2,10,控制表达式,11,控制流,12,Switch语句,13,函数,14,指针和数组,15,结构体和联合体,16,预处理指令,17,标准库,2,3,背景,C语言是开发嵌入式应用的主要
2、工具,然而C语言并非是专门为嵌入,式系统设计,相当多的嵌入式系统较一般计算机系统而言对软件安全,性(可靠性)有更苛刻的要求,所以因此会带来更多的安全隐患。,阿丽亚娜5型火箭爆炸瞬间,2020/7/5,4,1996年6月4日,阿丽亚娜5型运载火箭的首航,原计划将运送4颗,太阳风观察卫星到预定轨道,但因软件引发的问题导致火箭在发射39,秒后偏轨,从而激活了火箭的自我摧毁装置。阿丽亚娜5型火箭和其,他卫星在瞬间灰飞烟灭。(见图3),后来查明的事故原因是:代码重用。阿5型的发射系统代码直接重用,了阿4型的相应代码,而阿4型的飞行条件和阿5型的飞行条件截然不,同。此次事故损失3.7亿美元,软件缺陷的代价
3、极其昂贵。2002年,美国国家标准与技术研究所,的一项研究表明,软件缺陷给美国每年造成的损失高达595亿美元。,想想全球这个数额会是多大。,2020/7/5,5,编码规范的意义:,C语言在铁路行业的应用,安全可靠性非常重要,所以遵守一定的,编程规范的显得尤为重要,公司的C语言编码规范(安全系统),是基于2004年的GuidelinesForTheUseOfTheCLanguageinCritical,Systems而编写。,1、养成良好的编码习惯,摒弃那些可能存在风险的编程行为。编,写出安全健壮的代码,进而保证我们产品的可靠性、稳定性、安全,性;,2、增加软件的可读性,便于日后维护;,3、遵循
4、良好的共通的编码规范,也是项目开发中相互协作开发的,技术基础。,2020/7/5,6,规则2.2(强制)源代码只能采用/*/风格的注释。(MISRAC:2004-Rule,2.2),规则2.3(强制)字符序列/*不能在注释中使用。(MISRAC:2004-Rule2.3),任何在注释中出现的“/*“字符序列都是违背本规则的。,#includec_standards.h,*,*禁止使用嵌套的注释。,*/,voidstatic_function(void),*,*这样的字符序列不符合规则,*,2020/7/5,7,规则5.1(强制)内部或外部的标识符的识别都不能依赖于31个字符之,后的差异。(MI
5、SRAC:2004-Rule5.1),便于编译器识别,代码清晰易读,并保证可移植性。,规则5.2(强制)内部标识符不能使用和外部标识符相同的名字,导致,在内部范围内隐藏外部标识符。(MISRAC:2004-Rule5.2),示例:,int16_ti;,voidf(void),int16_ti;/*这是一个不同的内部变量*/,i=3;/*代码不易理解,不知道这个i指那个变量*/,国军标与本规则对应的准则:过程名禁止被重用;禁止参数与全局变量,同名。如,下例:,2020/7/5,8,#include“c_standards.h”,voidfoo(uint32_tP_1),uint32_tx=P_l
6、;,voidstatic_function(void),uint32_tfoo=1u;/*过程名静止被重用。*/,#includec_standards,/*-GlobalDeclarations*,uint32_tdwTmp=0;,voidstaticp(uint32_tdwTmp)/*禁止参数与全局变量同名。*/,*/,h,2020/7/5,9,规则6.1(强制)普通char类型只能用来存储字符值。(MISRAC:2004-,Rule6.1),规则6.2(强制)signed和unsignedchar只能作为数据值存储和使用。,(MISRAC:2004-Rule6.2),有三种方式声明cha
7、r类型:signedchar;unsignedchar和char类型。前,两种只能用于数值型数据,后一种用于字符数据。char类型是否具有符,号特性与具体的编译器有关。,对于声明为char类型的变量,只有三种操作符可以使用,即:=,,=,!=。,规则6.3(强制)位域只能被定义为unsignedint或者signedint类型。,(MISRAC:2004-Rule6.4),本规则与编译器的以下不确定行为有关:,未定义的行为:位域中的元素使用signedint/unsignedint以外的类型定,义。,用int定义位域中的元素时,可能被处理为signedint或者unsignedint,,取决于
8、具体的编译器。,2020/7/5,10,规则6.4(强制)signedint类型的位域元素的长度至少为2bit。(MISRA,C:2004-Rule6.5),1bit的signedint类型是没有意义的。,structbs,signedinta:1;/*只有一位没有意义,没有符号位*/,signedintb:2;/*符合规则*/,signedintc:3;/*符合规则*/,bit,*pbit;,规则7.1(强制)禁止使用八进制数及八进制转义序列。(MISRAC:2004-,Rule7.1),2020/7/5,11,规则8.1(强制)函数都应该有原型声明,且对函数定义和调用可见。,(MISRAC
9、:2004-Rule8.1),要求程序使用原型声明,是利用编译器检查函数调用时数据类型的一,致性。如果调用函数时,没有进行原型声明,则编译器不会检查出函,数形参与实参的个数、类型等的不一致。函数接口问题已经被证明是,一些重大软件问题的原因,因此本条规则尤为重要。,对外部函数来说,我们建议采用如下方法,在头文件中声明函数(亦,即给出其原型),并在所有需要该函数原型的代码文件中包含这个头,文件(见规则8.8)。为具有内部链接的函数给出其原型也是良好的,编程实践。,2020/7/5,12,*这样的字符序列不符合规则,p15=0;/*不允许,p1不是指向数组的指针*/,casee_wakeup:,即给
10、出其原型),并在所有需要该函数原型的代码文件中包含这个头,t=k+1;/*k在定义前被使用*/,选择是在最外一层的块域还是在最内一层的块域声明对象,很大程度,if(s8a5)/*不符合规则:恒为TRUE*/,signedintb:2;/*符合规则*/,while和for语句的主体必,不存在,而另一对象B仍然存在的情况,那么不允许将A的地址赋给对,staticfoo(void);/*错误,没有类型说明*/,C:2004-Rule12.,do_more();/*不符合规则:不能到达的代码*/,(MISRAC:2004-Rule16.,(MISRAC:2004-Rule14.,规则8.2(强制)无论
11、对一个对象和函数声明或定义时,它的类型都应,该显式声明。(MISRAC:2004-Rule8.2),示例:,externx;/*错误,没有类型说明*/,externint16_tx;/*正确*/,consty;/*错误,没有类型说明*/,constin16_ty;/*正确*/,staticfoo(void);/*错误,没有类型说明*/,staticint16_tfoo(void);/*正确*/,2020/7/5,13,规则8.3(强制)每个函数声明中的参数类型及返回值应与函数定义,一致。(MISRAC:2004-Rule8.3),函数声明中的参数类型及返回值应与函数定义中的一致,包括typed
12、ef,名称、限定词都需一致,而不仅仅是基本类型一致。,#includec_standards.h,FLOAT_32static_function(UINT_32,UINT_16);,SlNT_32static_function(UINT_32p_l,UINT_16p_2),/*定义的函数返回值与声明中不一致*/,SINT_32result=0;,/*,returnresult:,2020/7/5,14,规则8.5(强制)头文件中不要定义对象或者函数。(MISRAC:2004-,Rule8.5),如果该头文件被多个文件包含,会产生重复定义错误。当源文件包含,某一头文件时,预处理器会将头文件的内容
13、在包含指令处展开。显然,,在头文件中函数的定义会在其他源文件中一模一样的出现,导致函数被,重复定义。解决这一问题的关键是明确一个概念:所有可执行的代码或,者对象和函数的定义都应在.C的源文件中,头文件中只能存在其声明。,具体的做法是:为全局变量的声明增加extern修饰符,并在相应的.C,源文件中定义对象或函数。,/*在Globle.h中*/,externuint32_tGCounter;,/*在GlobleVariables.C中*/,uint32_tGCounter;,2020/7/5,15,规则8.7(强制)对象如果只被单个函数访问,应定义在块作用域。,(MISRAC:2004-Rule
14、8.7),对象的作用域应尽可能限制在单个函数内,只有在对象需要内部或外,部链接属性时才可在文件域声明,此时应遵守规则8.10的约束,即在,文件域声明的对象应尽可能具有内部链接属性。如非必要,好的做法,是尽量避免全局变量。,选择是在最外一层的块域还是在最内一层的块域声明对象,很大程度,上是编码风格的问题。,规则8.8(强制)一个外部变量或者函数只能在文件中被声明一次。,(MISRAC:2004-Rule8.8),一般来讲,对于外部变量和函数的声明都放在一个头文件中,任何定,义或引用这些外部对象的源文件需包含这个头文件。,示例:,/*在FeatureX.h中*/,externint16_ta;,/
15、*在FeatureX.c中*/,2020/7/5,int16_ta=0;,16,规则8.10(强制)所有在文件域中声明的对象或函数应具有内部链接属,性(除非必须在别的文件中使用这个对象或函数)。(MISRAC:2004-Rule,8.10),如果变量仅在本文件的函数中使用,则应使其具有内部链接属性,即对,变量的声明使用static关键字。同理,如果一个函数仅在本文件的其它函,数中调用,也使用static关键字。使用static声明是为了确保该标识符仅,对本文件可见,因而避免相同的标识符在其它文件或库文件中出现而引,起的混淆。,规则8.12(强制)如果一个数组具有外部链接属性,那么它的大小需显,
16、式说明,或者通过初始化隐式定义。(MISRAC:2004-Rule8.12),示例:,externintarray2/*可以通过编译,但不满足本规则*/,externintarray310/*满足规则*/,intarray110;/*满足规则*/,intarray2=1,2,3;/*满足规则*/,2020/7/5,17,规则9.1(强制)所有自动对象变量在使用之前都应该赋值。(MISRA,C:2004-Rule9.1),本规则与编译器的以下不确定行为有关:,未定义的行为:一个存储属性为auto的对象在初始化之前的值。,本条款的目的是确保所有的变量在引用之前已经被写入了确定的值。,但本规则不要求
17、必须在定义的时候初始化。,根据ISO标准,如果没有显式的初始化,具有静态存储属性的变量默,认会自动初始化为0。但实际上许多嵌入式编译环境并没有执行这个,操作。,静态存储指一种存储属性,使用static关键字声明、或具有外部链接,属性的变量具有静态存储属性。具有auto存储属性的变量通常不会,自动初始化。,voidstatic_function(void),intt,k;,t=k+1;/*k在定义前被使用*/,2020/7/5,18,规则9.3(强制)枚举列表中,“=“只能用于第一个成员初始化(除,非所有的成员都已初始化)。(MISRAC:2004-Rule9.3),如果一个枚举列表没有显式的初
18、始化,C语言会从0开始递增为每个元,素自动赋值。,如果只为第一个元素显式的赋值,则后续元素从第一个元素的值开始,,自动依次递增。使用这种方法初始化枚举列表是本规则允许的,但需要,确认后续的值不会超过int表示的范围。,对所有元素显式的初始化也是本规则允许的,但不允许自动赋值和人工,赋值混和。同时,程序员应确认所有的值都在规定范围内。,示例:,enumcolorred=3,blue,green,yellow=5;/*不符合本规则*/,enumcolorred=3,blue=4,green=5,yellow=5;/*符合本规则*/,2020/7/5,19,规则10.1(强制)以下情况下,整型表达式
19、的值不允许隐式转换为其它不同,的底层类型:,1.整型操作数不是被扩充为更多位数的相同符号特性的整数;或,2.表达式是复杂表达式;或,3.表达式不是常数表达式,且是函数的参数;或,4.表达式不是常数表达式,且是函数的返回表达式。,(MISRAC:2004-Rule10.1),规则10.2(强制)以下情况下,浮点数表达式的值不允许隐式转换为其它不,同类型:,1.浮点型操作数不是被扩充为更多位数的同符号浮点数;,2.表达式是复杂表达式;,3.表达式是函数的参数;,4.表达式是函数的返回表达式。,(MISRAC:2004-Rule10.2),2020/7/5,20,规则10.3(强制)整数类型的复杂表
20、达式的结果只允许转换为(与表,达式底层类型相比)更窄的同符号特性的数据类型。(MISRAC:2004-,Rule10.3),规则10.4(强制)浮点类型的复杂表达式的结果只允许转换为更窄的,浮点类型。(MISRAC:2004-Rule10.4),规则10.5(强制)如果位操作符或移位操作符)作用于底层类,型为unsignedchar或者unsignedshort类型的操作数时,中间运算步骤,的结果必须立刻显式转换为预期的底层类型。(MISRAC:2004-Rule10.5),/*执行以下程序,result_8的值?*/,uint8_tport=0 x5aU;,uint8_tresult_8;,
21、result_8=(port)4;,result_8=(uint8_t)(port)4;,2020/7/5,result_16=(uint16_t)(uint16_t)port)4;,21,规则10.6(强制)所有无符号型的常量后必须加“U“。(MISRA,C:2004-Rule10.6),规则11.1(强制)禁止函数指针和除整型外的任何数据类型相互转换。,(MISRAC:2004-Rule11.1),规则11.2(强制)指向某一类型对象的指针,除整型、其它对象指针以,及void指针外,禁止与其余类型相互转换。(MISRAC:2004-Rule11.2),规则11.3(强制)指针转换过程中不允
22、许丢失指针的const,volatile属性。,(MISRAC:2004-Rule11.5),规则12.1(强制)表达式的值必须在任何求值顺序下保持一致。(MISRA,C:2004-Rule12.2),2020/7/5,22,规则12.2(强制)不允许将sizeof运算符作用于有side-effect的表达式,上。(MISRAC:2004-Rule12.3),int32_ti=0;,int16_tj=0;,j=sizeof(i=1234);,规则12.3(强制)逻辑运算(&和|)的右操作数不允许为具有side-,effect的表达式。(MISRAC:2004-Rule12.4),if(istr
23、ue|do_something_with_side_effects(),do_something;,2020/7/5,23,规则12.4(强制)逻辑运算符(“&”或“|”)的操作数必须是一个基,本表达式。(MISRAC:2004-Rule12.5),这里基本表达式包括标识符、常量和括号括起来的表达式。,规则12.5(强制)不允许对底层类型为有符号的类型进行位操作。,(MISRAC:2004-Rule12.7),规则12.6(强制)移位操作的右操作数只能在0和操作数的位数减1,之间。(MISRAC:2004-Rule12.8),规则12.7(强制)不允许对底层类型为无符号的表达式进行一元负,(“
24、-”)运算符。(MISRAC:2004-Rule12.9),规则12.8(强制)不允许使用逗号表达式。(MISRAC:2004-Rule12.10),2020/7/5,24,规则12.9(强制)不得以位的形式操作或表示浮点型数值。(MISRA,C:2004-Rule12.12),规则13.1(强制)赋值表达式不能用在需要布尔值的地方。(MISRA,C:2004-Rule13.1),uint8_tx,y;,if(x=y),foo();,规则13.2(强制)不允许对浮点数进行相等或者不相等的比较。,(MISRAC:2004-Rule13.3),2020/7/5,25,规则13.3(强制)for循环
25、的控制表达式不应包含浮点数类型。(MISRA,C:2004-Rule13.4),规则13.4(强制)for语句中的3个表达式只能和循环控制相关。,(MISRAC:2004-Rule13.5),规则13.5(强制)for循环中,循环计数变量不允许在循环体中修改。,(MISRAC:2004-Rule13.6),flag=1;,for(i=0;(i5)i+),/*/,flag=0;/*符合规则*/,i=i+3;/*不符合规则*/,2020/7/5,26,规则13.6(强制)禁止使用结果为常量的布尔表达式。(MISRAC:2004-,Rule13.7),如果使用一个布尔表达式,但这个表达式的结果可以证
26、明总为“真”,或总为“假”,这种情况很大可能是代码错误。,if(u16a0)/*不符合规则:恒为FALSE*/,if(u16a10),if(s8a5)/*不符合规则:恒为TRUE*/,2020/7/5,27,规则14.1(强制)不得遗留“不可到达”的代码。(MISRAC:2004-Rule,14.1),switch(event),casee_wakeup:,do_something();,break;/*绝对跳转*/,do_more();/*不符合规则:不能到达的代码*/,default:,/*/,break;,如果一个函数没有在任何地方被调用,则整个函数都是“不可到达”的。,2020/7/5
27、,28,规则14.2(强制)所有非空语句必须满足如下任意一条:,a)产生至少一个side-effect(side-effect指表达式执行后对程序运行环,境造成的影响。赋值语句、自增操作等都是典型的具有side-effect的,操作);,b)改变程序控制流。(MISRAC:2004-Rule14.2),x=3;/*不符合规则:x与3比较,然后结果被丢弃了*/,规则14.3(强制)一行中如果有空语句,那么该行只能有这条空语,句,除注释外不能有别的语句,并且在这条空语句前不能有注释,注,释必须在其后,用空白字符隔开。(MISRAC:2004-Rule14.3),;/*符合规则*/,/*不符合规则:
28、注释放在空语句之前*/;,;/*不符合规则:注释与空语句之间没有空白字符*/,规则14.4(强制)不得使用goto语句。(MISRAC:2004-Rule14.4),2020/7/5,29,规则14.5(强制)不得使用continue语句。(MISRAC:2004-Rule,14.5),规则14.6(强制)一重循环中最多只能出现一个break语句用于,结束循环。(MISRAC:2004-Rule14.6),规则14.7(强制)函数只能有一个出口,这个出口必须在函数末,尾。(MISRAC:2004-Rule14.7),规则14.8(强制)switch、while、do.while和for语句的主
29、体必,须是复合语句(即用大括号包含),即使该主体只包含一条语句。,(MISRAC:2004-Rule14.8),规则14.9(强制)if结构后面必须是一个复合语句(即用大括号包,含),else后面必须是一个复合语句(即用大括号包含)或者另一,个if语句。(MISRAC:2004-Rule14.9),2020/7/5,30,规则14.10(强制)if.elseif结构必须由一个else子句结束。,(MISRAC:2004-Rule14.10),这个原则与规则15.3中,switch语句必须有default分支的原则是一致,的。,规则15.2(强制)所有非空的switch子句都应该用break语句
30、结束。,(MISRAC:2004-Rule15.2),规则15.3(强制)switch的最后一个分支必须是default分支。(MISRA,C:2004-Rule15.3),规则15.4(强制)switch表达式的值不能是布尔值。(MISRAC:2004-,Rule15.4),例如,以下例子是不符合本规则的:,switch(x=0)/*不符合规则:x=0是布尔值表达式*/,2020/7/5,31,规则15.5(强制)每条switch语句必须包含至少一个case分支。(MISRA,C:2004-Rule15.5),switch(x),uint8_tvar;/*不符合规则:在第一个case语句之前
31、定义*/,case0:,a=b;,break;/*这里的break是必须的*/,case1:/*空的分支中可以不用break*/,case2:,a=c;,if(a=b),case3:,/*不符合规则:case3最内的上层复合语句是if的主体*/,case4:,a=c;/*不符合规则:不为空的分支必须以break结束*/,default:/*必须有default语句*/,errorflag=1;/*default分支尽量不为空*/,break;,2020/7/5,32,规则16.1(强制)不允许定义参数数量不确定的函数。(MISRAC:2004-,Rule16.1),规则16.1(强制)不允许定
32、义参数数量不确定的函数。(MISRAC:2004-,Rule16.1),规则16.3(强制)在函数的原型声明中应给出所有形参的标识符。,(MISRAC:2004-Rule16.3),规则16.4(强制)在函数声明和定义中使用的标识符应当一致。,(MISRAC:2004-Rule16.4),规则16.5(强制)无形参的函数应将形参类型声明为void类型。,(MISRAC:2004-Rule16.5),2020/7/5,33,规则16.7(强制)非空返回值的函数的所有退出路径必须有一显式的,带表达式的return语句。(MISRAC:2004-Rule16.8),规则16.8(强制)函数标识符只允
33、许以下两种用法:使用一个&前缀+,函数标识符,或者函数标识符+(参数列表)。参数列表可以为空。,(MISRAC:2004-Rule16.9),if(fun)/*不符合规则*/,/*.*/,2020/7/5,34,规则17.1(强制)只有指向数组的指针才允许进行指针算术运算。,(MISRAC:2004-Rule17.1),此处的指针算术运算只仅仅限定于指针加减某个整数,比如ppoint=,ppoint-5,ppoint+等。对于不是指向类似array对象的元素的指针,执,行加或减的操作是没有定义的。,即使是指向array对象元素的指针,由于不能保证运算后得到的是有效,的地址(比如超过数组长度),
34、所以对指针进行代数运算是不安全的,,应谨慎对指针进行代数运算。,规则17.2(强制)只有指向同一个数组的两个指针才允许相减。(MISRA,C:2004-Rule17.2),指针的相减操作只有在它们指向同一个array对象时,结果才是充分定义,的,例如,UINT_16*q=p+i,那么可以通过q-p得到i的值。两个指针,可指向同一数组的不同成员。,2020/7/5,35,规则17.3(强制)只有指向同一个数组的两个指针才允许用、=、,、=等关系运算符进行比较。(MISRAC:2004-Rule17.3),规则17.4(强制)只允许用数组索引(下标)做指针运算。(MISRA,C:2004-Rule
35、17.4),uint8_ta10;,unit8_t*p;,p=a;,*(p+5)=0/*不符合规则:指针显式运算*/,p5=0/*符合规则*/,2020/7/5,36,voidmy_fun(uint8_t*p1,uint8_tp2),uint8_tindex=0;,uint8_t*p3;,uint8_t*p4;,*p1=0;,p1+;/*不允许,p1不是指向数组的指针*/,p1=p1+5;/*不允许,p1不是指向数组的指针*/,p15=0;/*不允许,p1不是指向数组的指针*/,p3=/*不允许:p1不是指向数组的指针*/,p20=0;,index+;,index=index+5;,p2ind
36、ex=0;/*允许*/,*(p2+index)=0;/*不允许*/,p4=/*允许*/,2020/7/5,37,规则17.6(强制)设对象A的存储特性为auto,如果可能发生A已经,不存在,而另一对象B仍然存在的情况,那么不允许将A的地址赋给对,象B。(MISRAC:2004-Rule17.6),int8_t*foobar(void),int8_tlocal_auto;,return(/*不符合规则*/,规则18.2(强制)不能将对象赋值给与之内存区域重叠的另一个对象。,(MISRAC:2004-Rule18.2),规则18.4(强制)不允许使用联合体。(MISRAC:2004-Rule18.
37、4),2020/7/5,38,规则20.3(强制)传给标准库函数的值的有效性需要检查。(MISRA,C:2004-Rule20.3),规则20.4(强制)动态内存分配不能使用。(MISRAC:2004-Rule20.4),规则19.1(强制)#include预处理指令只能跟或”filename”。,(MISRAC:2004-Rule19.3),规则20.7(强制)setjmp宏及longjmp函数不能使用。(MISRAC:2004-,Rule20.7),规则20.9(强制)产品代码中禁止使用输入/输出库函数。,(MISRAC:2004-Rule20.9),规则20.10(强制)stdlib.h中的库函数atof,atoi及atol不能使用。,(MISRAC:2004-Rule20.10),2020/7/5,39,谢谢大家!,2020/7/5,40,