《MISRA-C 2004工业标准化的C编程规范》笔记——背景、范围、简介

来源:互联网 发布:软件开发文档模板 编辑:程序博客网 时间:2024/05/22 15:04

目录

  • 目录
  • MISRA-C背景
  • MISRA-C范围
  • 使用MISRA-C
  • 规则简介
  • 规则

MISRA-C背景

【应用领域】
汽车工业领域的实时嵌入式应用。
【安全相关系统中的C使用】
不建议再安全相关系统中使用汇编语言。
【标准化】
MISRA-C-2004使用的标准是有ISO9899:1990定义的C编程语言(即C90)。
【MISRA-C的目标】
为促进钙语言的最为安全的使用。自立(Free-standing)的环境。

MISRA-C范围

【MISRA-C应用范围】
排除以下方面使用:
1、K&R C(Kernighan和Ritchie编写的“The C Programming language”的第一版)
2、C++
3、C的私有扩展
【可应用性】
MISRA-C 2004被设计为应用在汽车嵌入式系统的产品代码上。
在进行编译器和静态工具的比较检测(benchmark)是也不需要完全应用MISRA-C2004文档规则,有时在比较工具时由必要打破这些规则,以测量工具的响应。
【C++问题】
C++注释不应用在C代码中。尽管C编译器支持这种形式的注释(//)。

使用MISRA-C

【软件工程环境】
使用编程语言产生源代码只是软件开发过程的一个活动。如果只有在这样的活动中只坚持最佳实践而不考虑其他开发问题,只能带来有限的价值。部分问题如下:
1、文档化开发过程
2、能够满足IS0 9001/ISO 90003/ Tick IT[11/12/13]标准的质量体系
3、项目管理
4、配置管理
5、风险分析(Hazard Analysis)
6、需求
7、设计
8、编码
9、确认(Verification)
10、有效性验证(Validation)
软件开发者有必要证明他们整个开发过程对其所开发的系统来说是合适的。这样的声明只有当执行完确定系统安全集成度的风险分析活动后才能完成。
【编程语言和编码环境】
在软件开发过程的编程阶段,语言子集只是许多方面之一,而如果不考虑其他开发问题,只在这一方面中坚持最佳实践,只能带来有限的价值。进阶语言的选择后,其他关键问题:
1、培训(Training)
为确保C代码编写者具有合适的熟练程度和能力,应该提供正规训练:
a、嵌入式应用中C编程语言的使用;
b、高度集成的和安全相关的系统中C编程语言的使用;
c、用于强制遵循子集的静态检查工具的使用
2、风格指南(Style gudie)
一个组织应该有其自己的实验室内的编程风格,包含那些不直接影响代码正确性但定义了代码呈现形式的实验室风格(House style)的问题(主观性)。风格指南涉及的典型问题:
a、代码布局和缩进的使用。
——缩进4位。
b、大括号{}和结构块的布局
c、语句的复杂性
——单一功能函数原则。
——单一语句单一功能原则。
详细的源代码度量参见“ Software Metrics: A Rigorous and Practical Approach”( Fenton 和
Pfleeger),以及 MISRA 关于软件度量的报告
d、命名规范。
——函数名:“模块+动宾”结构。
——全局变量:“命名+g_”。
e、注释语句的使用。
——不使用//,仅使用/**/。
——不使用的函数不使用/**/注释掉,而使用条件编译
f、公司名称、版权提示和其他标准文件头信息的包含。
对于风格指南,某些内容是建议性的,某些是强制性。
3、编译器选择和有效性验证
4、检查工具的验证
开发者可以采用的增强工具信心的可能手段:
※执行某种形式的文档化验证测试
※评估工具供应商的软件开发过程
※检阅工具迄今为止的性能
5、度量
6、测试覆盖率
在软件设计和编写之前,应该定义好他期望达到的语句覆盖率。Design For Test(DFT)。
使用子集,减少定义实现的属性的数量和增加模块接口兼容性,能产生可以进行较大规模集成和测试的软件。折中考虑以下的度量可以促进语句覆盖率的实现:
※代码规模
※圈复杂度
※静态路径数目
7、在这些问题上作出的所有选择及其原因都要记录成文档,并对任何的活动保持适当的记录

规则简介

【规则分类】
1、强制required。
2、建议advisory。
【Annex G 参考】
1、Unspecified,未指明的。——必须成功编译的语言结构
2、Undefined,未定义的。——本质的编程错误
3、Implementation-defined,实现定义。——类似于“unspecified”问题,其主要区别在于编译器要提供一致的行为并记录成文档,尽可能避免这些问题
4、Locale-specfic,语言环境特效。——与国际标准有所不同的一些小的特性

规则

【环境】
规则1.4(强制)编译器/连接器要确保31个有效字符和大小写敏感能被外部标识符支持
1、ISO 标准要求外部标识符的头 6 个字符是截然不同的。
2、大多数编译器/链接器允许至少 31 个有效字符(如同内部标识符)。
规则1.5(建议)浮点应用应该适应于已定义的浮点标准
1、一些问题可以通过适应已定义的标准来克服。其中一个合适的标准是ANSI/IEEE Std 754。
浮点类型的定义提供了一个注释所用浮点标准的机会,如:

/* IEEE 754 single-precision floating-point */typedef float float32_t;

【语言扩展】
规则2.1(强制)汇编语言应该被封装并隔离
建议如下方式封装并隔离这些指令:
1、汇编函数
2、C语言
3、。最好使用宏来完成。
规则2.2(强制)源代码应该使用/**/类型的注释
//这样的C99类型的注释和C++类型的注释,在C90中是不允许的。
规则2.3(强制):字符序列/*不应出现在注释中。
C不支持注释的嵌套。
规则2.4(建议·):代码段不应被注释掉(comment out)。
源代码不需要被编译时,应该使用条件编译来完成(如带有注释的#ifhuo1#ifdef结构)。
原因:C不支持嵌套的注释,已经存在于代码段的任何注释将影响执行的结果。

【文档】
规则3.1(强制):所有实现定义(implementation-defined)的行为的使用都应该文档化。
规则3.3(建议):应该确定、文档化和重视所选编译器中整数除法的实现。
当两个有符号整型数做除法时, ISO 兼容的编译器的运算可能会为正或为负。首先,它可能以负余数向上四舍五入(如, -5/3 = -1,余数为-2),或者可能以正余数向下四舍五入(如,-5/3 = -2,余数为+1)。重要的是要确定这两种运算中编译器实现的是哪一种,并以文档方式提供给编程人员,特别是第二种情况(通常这种情况比较少)
规则3.4(强制):所有#pragma指令的使用应该文档化并给出良好解释。
应当尽量减少任何pragma的使用,尽可能地把它们本地化和封装成专门的函数。
规则3.5(强制):如果作为其他特性的支撑,实现定义(implementation-defined)的行为和位域(bitfields)集合应当文档化。
这是在使用了规则 6.4 和规则 6.5 中描述的非良好定义的位域时遇到的特定问题。 C 当中的位域是该语言中最缺乏良好定义的部分之一。位域的使用可能体现在两个主要方面:z 为了在大的数据类型(同 union 一起)中访问独立的数据位或成组数据位。该用法是不允许的(见规则 18.4)。z 为了访问用于节省存储空间而打包的标志( flags)或其他短型( short-length)数据。为了有效利用存储空间而做的短型数据的打包,是本文档所设想的唯一可接受的位域使用。假定结构元素只能以其名字来访问,那么程序员就无需设想结构体中位域的存储方式。我们建议结构的声明要保持位域的设置,并且在同一个结构中不得包含其他任何数据。要注意的是,在定义位域的时候不需要追随规则 6.3,因为它们的长度已经定义在结构中了。如果编译器带有一个开关以强制位域遵循某个特定的布局,那么它有助于下面的判断。
例如下面可接受的代码:
struct message /* Struct is for bit-fields only */
{
signed int little: 4; /* Note: use of basic types is required */
unsigned int x_set: 1;
unsigned int y_set: 1;
}message_chunk;
如果要使用位域,就得注意实现定义的行为所存在的领域及其潜藏的缺陷(意即不可移植性)。特别地,程序员应当注意如下问题:z 位域在存储单元中的分配是实现定义( implementation-defined)的,也就是说,它们在存储单元(通常是一个字节)中是高端在后( high end)还是低端在后( low end)的。z 位域是否重叠了存储单元的界限同样是实现定义的行为(例如,如果顺序存储一个 6位的域和一个 4 位的域,那么 4 位的域是全部从新的字节开始,还是其中 2 位占据一个字节中的剩余 2 位而其他 2 位开始于下个字节)。

【字符集】
规则4.1(强制):只能使用ISO C标准中定义的escape序列。
escape sequence:用来生成转码符的关键字的顺序。
规则4.2(强制):不能使用三字母词。
【标识符】
规则5.1(强制):标识符(内部的和外部的)的有效字符不能多于31。
规则5.2(强制):具有内部作用域的标识符不应使用与外部作用域的标识符相同的名称,这会隐藏了外部标识符。
规则5.3(强制):typedef的名字应当是唯一的标识符。
规则5.4(强制):标签(tag)名称必须是唯一的标识符。
【类型】
规则6.1(强制):单纯的char类型应该只用做存储和使用字符值。
规则6.2(强制):signed char 和 unsigned char类型应该只用做存储和使用数字值。
有3种不同的char类型:char、unsigned char 、signed char。
unsigned char 用于数字型数据,
char 用于字符型数据,单纯cha类型的符号是实现定义的,不应依赖。
单纯的char类型所能接收的操作只有赋值和等于操作符(=、==、!=)。
规则6.3(建议):应该使用指示了大小和符号的typedef以代替基本数据类型。
不应使用基本数值类型char/int/short/long/float/double,而应使用特定长度(specific-length)的typedef.
比如,本文档中建议为所有基本数值类型和字符类型使用如下所示的 ISO( POSIX)的
typedef。对于 32 位计算机,它们是:
typedef char char_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
typedef float float32_t;
typedef double float64_t;
typedef long double float128_t;
在位域类型的说明中, typedef 是不必要的。
规则6.4(强制):位域只能被定义为unsigned int 或 signed int类型。
因为int类型的位域可以是signed 或 unsigned,使用int是由实现定义的,由于其行为未被定义,所以不允许为位域使用enum、short或char类型。
规则6.5(强制):unsigned int 类型的位域至少应该是2bits长度。
1bit长度的有符号位域是无用的。

原创粉丝点击