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