MISRA C 2012标准学习与理解

您所在的位置:网站首页 int8和int16的区别 MISRA C 2012标准学习与理解

MISRA C 2012标准学习与理解

#MISRA C 2012标准学习与理解| 来源: 网络整理| 查看: 265

目录总览指示(Directives)实现编译和构建需求可追踪性代码设计规则(Rules)标准C环境未使用代码(Unused code)注释(Comments)字符集和词汇约定(Character sets and lexical conventions)标识符(Identifiers)类型(types)文字和常量(Literals and constants)声明和定义( Declarations and definitions)初始化(Initialization)基本类型模型(The essential type model)指针类型转换(Pointer type conversions)表达式(Expressions)副作用(Side effects)控制状态表达式(Control statement expressions)控制流(Control flow)switch语句(Switch statements)函数(Functions)指针和数组(Pointers and arrays)存储重叠(Overlapping storage)预处理指令(Preprocessing directives)标准库(Standard libraries)资源(Resources)参考

总览

MISAR C是汽车行业C编码标准,由汽车产业软件可靠性协会(MISRA)发布,目的在提升嵌入式系统的安全性和可移植性。针对C++,也有对应标准MISRA C++。本文主要对MISRA C 2012标准进行介绍,扩展对部分条款的理解。

2012发布MISRA C第三版,称为MISRA C:2012。 MISRA C不能100%确保程序不出问题,但能有效预防编程带来的问题。MISRA C具有以下优势: 1)提升可靠性; 2)提升可读性; 3)提升可移植性; 4)提升可维护性; 5)提升安全性。

指示、规则汇总统计:

分类 强化执行 完全实现 部分实现 未实现 不可静态检查 总计 强制 0 8 2 0 0 10 要求 9 92 8 0 2 111 建议 7 25 6 0 0 38 总计 16 125 16 0 2 159 指示(Directives) 实现 Dir 1.1 Any implementation-defined behaviour on which the output of the program depends shall be documented and understood

要求。应该用文档记录并了解程序输出依赖的任何实现定义行为。

编译和构建 Dir 2.1 All source files shall compile without any compilation errors

要求。所有源文件必须没有任何编译错误。

需求可追踪性 Dir 3.1 All code shall be traceable to documented requirements

要求。所有代码应该可以追溯至文件要求。

代码设计 Dir 4.1 Run-time failures shall be minimised

要求。运行时故障必须最小化。

Dir 4.2 All usage of assembly language should be documented

建议。所有汇编的使用应当用文档记录。

Dir 4.3 Assembly language shall be encapsulated and isolated

要求。汇编语言必须封装、隔离。

Dir 4.4 Sections of code should not be 'commented out'

建议。代码部分不应当注释掉。

例如,不应该用"// ... "或者"/* ... */" 注释代码。而应该用"#ifdef ...#endif"等预编译指令。

Dir 4.5 Identifiers in the same namespace with overlapping visibility should be typographically unambiguous

建议。同一命名空间中,具有重叠可见性的标识符,必须在排版上毫不含糊。

类似于C++名称遮掩问题。例如,全局变量不要与局部变量重名。

Dir 4.6 typedefs that indicate size and signedness should be used in place of the basic numerical types

建议。指示大小、符号的typedefs(类型定义),应当用来替代基本的数字类型。

例如,typedef定义的uint32_t,用来替代32位无符号整型。

Dir 4.7 If a function returns error information, then that error information shall be tested

要求。如果一个函数返回错误信息,那么错误信息应答被测试。

Dir 4.8 If a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hidden

建议。如果一个指向struct或union的指针,在翻译单元内从未被解引用,则应该隐藏该对象的实现。

例如,结构体A实现如下:

// A.h struct A { uint32_t id; uint8_t name[32]; };

如果test.c中,从未对指向A类型对象的指针进行解引用,也就没有访问其成员。此时,在test.c文件中应该使用前向声明,而不应该使用"#include "。

// test.c // 正确用法:前向声明 struct A; // include 不必要 #include Dir 4.9 A function should be used in preference to a function-like macro where they are interchangeable

建议。在函数、函数类的宏可以相互替换的地方,应优先使用函数。

因为函数会在编译期做类型检查,更安全。

Dir 4.10 Precautions shall be taken in order to prevent the contents of a header file being included more than once

要求。应当采取预防措施,防止头文件的内容被多次包含。

通常,使用"#ifndef ... #endif"。也可以使用"#pragma once",不过想要编译器支持。

Dir 4.11 The validity of values passed to library functions shall be checked

要求。传递给库函数的值的有效性,应当被检查。

因为一些库函数有严格的限制域,需要检查: 1)许多数学函数(中的math函数),如: (1)负数不允许传递给sqrt, log函数。 (2)fmod第二个参数不应为0。

2)当非小写字母的参数传递给函数toupper时(类似有tolower),一些实现能产生非预期结果。 3)中的字符测试函数,在传递无效值时,表现出未定义行为。如,isalnum, isalpha, islower等。 4)给abs函数传递最值负数时,表现出未定义行为。最小负数值转换成整数值,由于位宽限制,无法正确转换。

Dir 4.12 Dynamic memory allocation shall not be used

要求。不应该使用动态内存分配。

例如,不应该使用malloc/free进行动态内存分配。

Dir 4.13 Functions which are designed to provide operations on a resource should be called in an appropriate sequence

建议。设计用来提供操作资源的函数,应当以合适的序列进行调用。

例如,某个硬件模块的操作,应当遵循一定顺序,以符合硬件资源特性。

规则(Rules) 标准C环境 Rule 1.1 The program shall contain no violations of the standard C syntax and constraints, and shall not exceed the implementation's translation limits

要求。程序不得违反标准C语法和约束,不应超出实现的翻译限制。

什么意思? 程序必须只使用C语言特性及其库。除非使用语言扩展,否则程序不应: 1)包含任何违反本标准中描述的语言语法行为; 2)包含任何违反本标准规定的限制行为。

例如, 语法行为:不支持写const变量。 语言扩展:一些C90编译器提供__inline关键字声明inline函数。许多编译器支持使用一些关键字定义对象位置,如__zpage, __near, __far。

Rule 1.2 Language extensions should not be used

建议。语言扩展不应使用。

编译器通常会提供一些标准C(ISO 标准C)以外的语言特性。GCC可以用'-pedantic'选项,打印使用了这些特性的警告信息。

例如,GNU C提供的语言扩展: 1)表达式中的声明

// case 1: 函数参数表达式 ({ int y = foo(); int z; if (y > 0) z = y; else z = -y; z; }) // case 2 #define maxint(a,b) \ ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) // case 3: C++中 A a; ({a;}).Foo()

2)本地声明标签 local作用域内的标签声明。标签通常搭配goto使用。 例如,下面的found就是local标签:

#define SEARCH(value, array, target) \ do { \ __label__ found; \ typeof (target) _SEARCH_target = (target); \ typeof (*(array)) *_SEARCH_array = (array); \ int i, j; \ int value; \ for (i = 0; i < max; i++) \ for (j = 0; j < max; j++) \ if (_SEARCH_array[i][j] == _SEARCH_target) \ { (value) = i; goto found; } \ (value) = -1; \ found:; \ } while (0)

3)标签作为值 可以用单目操作符"&&",得到在当前函数内定义的标签地址。

void *ptr; ptr = &&foo; ... goto *ptr; // 跳转到foo函数

4)嵌套函数 一个函数嵌套定义在另一个函数内。

foo (double a, double b) { double square(double z) { return z * z; } // square是嵌套函数 return square (a) + square(b); }

5)构建函数调用 使用编译器内置函数,记录函数收到的参数,调用另一个具有相同参数的函数,而无需知道参数的数值或类型。

6)参考typeof定义的类型 一个类型,无需知道具体类型,可以通过数据推导出类型。 例如,

typeof (x[0](1)) typeof (int*) #define max(a,b) \ ({ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a > _b ? _a : _b; })

7)带省略操作的条件表达式 三元表达式的条件表达式,中间操作数可省略。

x ? : y x ? x : y

8)128-bit整型

signed __int128 // 有符号128-bit整型 unsigned __int128 // 无符号128-bit整型

9)双字整型 C99支持数据类型long long int,对应至少64bit宽度,而在C90中,GCC将其作为扩展实现。

10)复数

_Complex // 关键字 __complex__ // 旧版本GNU关键字

等等GNU扩展C语言特性。

Rule 1.3 There shall be no occurrence of undefined or critical unspecified behaviour

要求。不应出现未定义或关键的未指定行为。

未使用代码(Unused code) Rule 2.1 A project shall not contain unreachable code

要求。项目不应包含不可达代码。

例如,

void foo() { int a = 1; if (a != 0) { ... } else { // 这里else表达式不可达 ... } } Rule 2.2 There shall be no dead code

要求。不应包含死代码。

什么是死代码? 任何已执行,但其擅长不会影响程序行为的代码,都会构成死代码。而由语言扩展引入的操作被认为总是对程序行为有影响的。 例如,下面函数就是死代码,因为不会影响程序行为。

extern volatile uint16_t v; extern char *p; void f ( void ) { uint16_t x; ( void ) v; /* Compliant - v is accessed for its side effect * and the cast to void is permitted * by exception */ ( int32_t ) v; /* Non-compliant - the cast operator is dead */ // 死代码 v >> 3; /* Non-compliant - the >> operator is dead */ // 死代码 x = 3; /* Non-compliant - the = operator is dead // 死代码 * - x is not subsequently read */ *p++; /* Non-compliant - result of * operator is not used */ (*p )++; /* Compliant - *p is incremented */ } __asm("NOP"); // __asm是语言扩展关键字, 因此并非死代码

另一种情况,函数并非死代码,但函数调用是死代码。

void g ( void ) // g是空函数, 非死代码 { /* Compliant - there are no operations in this function */ } void h ( void ) // 死代码 { g(); } Rule 2.3 A project should not contain unused type declarations

建议。项目不应包含未使用类型声明。

如果一个类型定义但未使用,审核人不清楚该类型是否冗余,或者遗留的未使用错误。例如,

int16_t unusedtype ( void ) { typedef int16_t local_Type; /* Non-compliant */ return 67; } Rule 2.4 A project should not contain unused tag declarations

建议。项目不应包含未使用标记声明。

// enum state未使用 void unusedtag ( void ) { enum state { S_init, S_run, S_sleep }; /* Non-compliant */ } // 没有使用struct record_t, 不建议声明 typedef struct record_t /* Non-compliant */ { uint16_t key; uint16_t val; } record1_t; // 使用了record2_t, 推荐声明 typedef struct /* Compliant */ { uint16_t key; uint16_t val; } record2_t; Rule 2.5 A project should not contain unused macro declarations

建议。项目不应包含未使用宏定义声明。

Rule 2.6 A function should not contain unused l abel declarations

建议。函数不应包含未使用标签声明。

Rule 2.7 There should be no unused parameters in functions

建议。函数中,不应有未使用参数。

如果确实定义了未使用的参数,为了确保函数兼容性,可以形参明省略

void withunusedpara ( uint1 6_t *para1, int16_t unusedpara) /* Non-compliant - unused */ { *para1 = 42U; } void withunusedpara ( uint1 6_t *para1, int16_t) // 推荐做法, 删去形参名 { ... } 注释(Comments) Rule 3.1 The character sequences /* an d // shal l not be used within a comment

要求。字符序列 /*和//不应在中使用。 例如,下面行为不符合该规则:

x = y // /* + z // */ ; Rule 3.2 Line-splicing shall not be used in // comments

要求。不得在注释//中使用续行符('')。

例如,下面行为不符合该规则:

extern bool_t b; void f ( void ) { uint16_t x = 0; // comment \ if ( b ) { ++x; /* This is always executed */ } } 字符集和词汇约定(Character sets and lexical conventions) Rule 4.1 Octal and hexadecimal escape sequences shall be terminated

要求。8进制和16进制高级转义序列应终止。

如果8进制或16进制转义序列后跟其他转义序列,可能出现混淆。如,字符常量'\x1f'由1个字符组成(表示ASCII 16进制值为1f的单元分隔符US),而'\x1g'由2个字符'\x1'(表示ASCII 16进制值为1的标题开始SOH)和'g'组成。

// 16进制转义序列, 以\x开头 const char *s1 = "\x41g"; /* Non-compliant */ const char *s2 = "\x41" "g"; /* Compliant - terminated by end of literal */ const char *s3 = "\x41\x67"; /* Compliant - terminated by another escape */ // 8进制转义序列, 以\1开头 int c1 = '\141t'; /* Non-compliant */ int c2 = '\141\t'; /* Compliant - terminated by another escape */ Rule 4.2 Trigraphs should not be used

建议。三字母词不应使用。

三字母词(Trigraphs)由两个问号的序列,跟着一个特殊的第三字符。例如,??- 表示~(波浪线),??) 表示 a]。它们可能与两个问号的其他用法混淆。 比如,字符串"(Date should be in the form ??-??-??)"会被编译器解释为"(Date should be in the form ~~]"。 Trigraphs会在预处理阶段被替换。

标识符(Identifiers) Rule 5.1 External identifiers shall be distinct

要求。外部标识符应该不同。

Rule 5.2 Identifiers declared in the same scope and name space shall be distinct

要求。声明在同一作用域和命名空间的标识符,应该是不同的。

Rule 5.3 An identifier declared in an inner scope shall not hide an identifier declared in an outer scope

要求。在内部作用域中声明的标识符,不应隐藏外部作用域中声明的标识符。

// 外部作用域中声明的标识符 extern int32_t engine_exhaust_gas_temperature_raw; // 内部作用域中声明的标识符 static int32_t engine_exhaust_gas_temperature_scaled; /* Non-compliant */ void f ( void ) { /* 1234567890123456789012345678901********* Characters */ int32_t engine_exhaust_gas_temperature_local; /* Compliant */ } Rule 5.4 Macro identifiers shall be distinct

要求。宏定义标识符应该不同。

Rule 5.5 Identifiers shall be distinct from macro names

要求。标识符应与宏定义名称不同。

Rule 5.6 A typedef name shall be a unique identifier

要求。typedef类型名应该是独有的标识符。

Rule 5.7 A tag name shall be a unique identifier

要求。标记名应该是独有的标识符。

例如,不应有与struct名称 同名的union类型;也不应该定义重名的标记名。

Rule 5.8 Identifiers that define objects or functions with external linkage shall be unique

要求。使用外部链定义对象或函数的标识符应该唯一。

不同源文件,不要存在标识符重名的情况。

Rule 5.9 Identifiers that define objects or functions with internal linkage should be unique

建议。使用内部链接定义的对象或函数应该唯一。

同一源文件,不要存在标识符重名的情况。

类型(types) Rule 6.1 Bit-fields shall only be declared with an appropriate type

要求。位域只能用适当的类型声明。

对于C90,适当的位域类型:unsigned int 或 signed int。 对于C99,适当的位域类型: 1)unsigned int 或 signed int; 2)实现所允许的另一个显式有符号或显式无符号整型; 3)_Bool。 注意:允许使用typedef来指定适当的类型。

反例:C90中,enum, short, char等不是适当的位域类型;而C99中,这些由整型实现,是适当的位域类型。

Rule 6.2 Single-bit named bit fields shall not be of a signed type

要求。单比特命名位字段不应为带符号类型。

注意:该规则不适用于匿名位域。

文字和常量(Literals and constants) Rule 7.1 Octal constants shall not be used

要求。不应使用8进制常数。

why? 因为很容易跟10进制常数混淆,比如52(10进制),052(8进制,对应10进制42)。

Rule 7.2 A "u" or "U" suffix shall be applied to all integer constants that are represented in an unsigned type

要求。"u"或"U"后缀应该应用到整型常量,代表无符号类型。

Rule 7.3 The lowercase character 'l' shall not be used in a literal suffix

要求。小写字母'l'不应用于文字后缀。

因为很容易跟数字'1'混淆。

Rule 7.4 A string literal shall not be assigned to an object unless the object's type is “pointer to const-qualified char”

要求。字符串文字不应赋值给对象,除非对象的类型是“指向常量限定字符的指针”。

因为字符串文字是常量,传递给non-const对象的话,用户修改内容可能会导致异常。 例如,

char *s = "string"; // 不符合该规则 const volatilechar *p = "string"; // 符合 extern void f1(char *s1); extern void f2(const char *s2); void g(void) { f1("string"); // 不符合 f2("string"); // 符合 } char *name1(void) { // 不符合 return ("MISRA"); } const char *name2(void) { // 符合 return ("MISRA"); } 声明和定义( Declarations and definitions) Rule 8.1 Types shall be explicitly specified

要求。类型应明确指定。

因为C90标准允许在特定情形下,省略类型,此时,int类型为隐式指定。可能使用隐式int的情况示例如下: 1)对象声明; 2)参数声明; 3)成员声明; 4)typedef 声明; 5)函数返回类型。

extern x; // 隐式int, 不符合该条规则 extern int16_t x; // 显式类型 const y; // 隐式int const uint16_t y; // 显式类型 extern f(void); // 隐式int extern int16_t f(void); // 显式类型 extern void g(char c, const k); // k 是隐式int类型 extern void g(char c, const int16_t k); // 显示类型 struct str { int16_t x; // 显式类型 const y; // 隐式int } s;

省略明确的类型可能导致混淆。例如:

extern void g(char c, const k); // k是const int, 但用户可能期望是const char Rule 8.2 Function types shall be in prototype form with named parameters

要求。函数类型应该为带命名参数的原型。

为了避免混淆,或者不一致。例如:

extern int16_t func1(int16_t n); // 符合 extern void func2(int16_t); // 不符合 static int16_t func3(); // 不符合 static int16_t func4(void); // 符合 Rule 8.3 All declarations of an object or function shall use the same names and type qualifiers

要求。所有对象或函数的声明应使用相同的名字或类型限定符。

相同基本类型的兼容版本可以互换,如int, signed, signed int 都是等价的。而const, non-const则不可以互换。

Rule 8.4 A compatible declaration shall be visible when an object or function with external linkage is defined

要求。当对象或函数由外部链接定义时,兼容声明应当是可见的。

定义外部对象或函数时,有一个定义,必须对应一个声明。

extern int16_t count; int16_t count = 0; /* Compliant */ // 符合该条规则 // 不符合, 因为定义之前没有声明 extern uint16_t speed = 6000u; /* Non-compliant - no declaration prior to this definition */ // 不符合, 因为定义之前没有声明 uint8_t pressure = 101u; /* Non-compliant - no declaration prior to this definition */ Rule 8.5 An external object or function shall be declared once in one and only one file

要求。外部对象或函数应在一个且仅在一个文件中声明。

也就是说,每个外部对象或函数,应当仅在一个.h文件中声明一次。

Rule 8.6 An identifier with external linkage shall have exactly one external definition

要求。外部链接的标识符应有一个外部定义。

包含2点:1)标识符必须有定义;2)定义只能有一个。

Rule 8.7 Functions and objects should not be defined with external linkage if they are referenced in only one

建议。如果函数和对象仅在一个翻译单元中引用,则不应由外部链接定义。

这是为了限制对象可见性。

Rule 8.8 The static storage class specifier shall be used in all declarations of objects and functions that have internal linkage

要求。静态存储类说明符 应该用于所有内部链接的对象和函数的声明。

Rule 8.9 An object should be defined at block scope if its identifier only appears in a single function

建议。如果对象的标识符仅出现在单个函数中,则应在块范围内定义该对象。

减少对象的可见性。

Rule 8.10 An inline function shall be declared with the static storage class

要求。内联函数应与静态存储类一同声明。

内联函数声明为外部链接,但没有在同一翻译单元内定义,会导致未定义行为。 调用外部链接的内联函数,可能调用外部函数的定义,或者使用内联定义,这会影响执行速度。

注意:可通过内联函数置于将头文件,使得内联函数在多个翻译单元内可用。

Rule 8.11 When an array with external linkage is declared, its size should be explicitly specified

要求。当外部链接的数组声明时,它的尺寸应明确指定。

该规则仅应用于非定义的声明。声明中明确数组尺寸,便于检查一致性,以及边界检查。例如,

extern int32_t array1[ 10 ]; /* Compliant */ extern int32_t array2[ ]; /* Non-compliant */ Rule 8.12 Within an enumerator list, the value of an implicitly specified enumeration constant shall be unique

要求。在枚举列表中,隐式指定枚举常量值是唯一的。

未指定值的枚举成员默认递增,注意不能与其他成员同值。

Rule 8.13 A pointer should point to a const-qualified type whenever possible

建议。指针应尽可能指向常量限定类型。

Rule 8.14 The restrict type qualifier shall not be used

要求。restrict类型限定符不应使用。

C中restrict关键字用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的防晒修改该对象的内容。当使用restrict类型限定符时,可能会改善由编译器生成的代码效率。但使用restrict同时,也要求程序员必须确保两个指针所指内存区域没有重叠。

初始化(Initialization) Rule 9.1 The value of an object with automatic storage duration shall not be read before it has been set

强制。自动变量在设置之前不允许读取。

Rule 9.2 The initializer for an aggregate or union shall be enclosed in braces

要求。聚合体或联合体的初值应该包含在大括号中。

聚合体是指数组(array)或类(class)或结构体(struct)。

注意:{ 0 }形式的初始化器,可以设置所有值为0,而无需嵌套括号。

例如,

int16_t y[ 3 ][ 2 ] = { 1, 2, 0, 0, 5, 6 }; /* Non-compliant */ int16_t y[ 3 ][ 2 ] = { { 1, 2 }, { 0 }, { 5, 6 } }; /* Compliant */ int16_t y[ 3 ][ 2 ] = { { 1, 2 }, { 0, 0 }, { 5, 6 } }; /* Compliant */ Rule 9.3 Arrays shall not be partially initialized

要求。数组不应部分初始化。

必须为每个数组元素设定初值。

例如,

// 符合这条规则 int16_t a1[ 5 ] = { -5, -4, -3, -2, -1 }; // 符合 int16_t a2[ 5 ] = { [ 0 ] = -5, [ 1 ] = -4, [ 2 ] = -3, [ 3 ] = -2, [ 4 ] = -1 }; // 不符合, 因为第2号元素重复指定值, 第3号元素未指定值 int16_t a3[ 5 ] = { [ 0 ] = -5, [ 1 ] = -4, [ 2 ] = -3, [ 2 ] = -2, [ 4 ] = -1 }; Rule 9.4 An element of an object shall not be initialised more than once

要求。对象的元素不应初始化超过一次。

Rule 9.5 Where designated initialisers are used to initialize an array object the size of the array shall be specified explicitly

要求。如果指定的初始化器用于初始化数组对象,那么应明确指定数组的大小。

// 不符合, 因为用初始化器初始化数组时, 没有明确指定大小 int a1[ ] = { [ 0 ] = 1 }; // 符合 int a2[ 10 ] = { [ 0 ] = 1 }; 基本类型模型(The essential type model) Rule 10.1 Operands shall not be of an inappropriate essential type

要求。操作数不应具有不恰当的基本类型。

算术操作数的基本类型类别:

Opeartor Operand Boolean character enum signed unsigned floating [ ] ineger 3 4 1 ++ 3 4 5 -- 3 4 5 8 + - either 3 5 * / either 3 4 5 % either 3 4 5 1 < > = either 3 == != either ! && !! any 2 2 2 2 2 > left 3 4 5,6 6 1 > right 3 4 7 7 1 ~ & ! ^ any 3 4 5,6 6 1 ?: 1st 2 2 2 2 2 ?: 2nd and 3rd

上面的数字对应下面原理编号: 1.对这些操作数使用floating类型,是违反约束的。 2.本质上Boolean类型的表达式,应该用于操作数解释为Boolean值的地方。 3.本质上Boolean类型的操作数,不应用于操作数解释为数值的地方。 4.本质上character类型的操作数,不应用于操作数解释为数值的地方。字符数据的数值是由实现定义的。 5.本质上enum类型的操作数,不应用于算术操作,因为enum对象使用整型定义实现。涉及枚举对象的操作,可能产生意外类型的结果。注意匿名枚举中的枚举常量,本质上具有带符号类型。 6.移位和逐位操作仅对本质上无符号的操作数执行。本质上有符号类型的使用产生的数值,是由定义实现的。 7.移位运算的右侧操作符,应该是本质上无符号类型,以确保负移位不会刀子划未定义行为。 8.本质上有符号类型的操作数,不应用于一元减号运算符的操作数,因为结果的符号性由实现的int大小决定。

Rule 10.2 Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations

要求。本质上为字符类型的表达式,不应用在不正确的加法和减法运算中。

当数据不代表数值时,带字符类型的表达式不能用于算术运算。然而有些情况是允许字符数据的运算的。 例如: 1)2个字符类型的操作数的减法,可用于在数字'0'到'9'和相应的序数之间转换。 2)字符类型和无符号类型的加法,可能用于序数到相应数字范围'0'至'9的转换。 3)从字符类型到无符号类型底减法,可能用于小写转大写。

实例,

'0' + u8a // 符合: 无符号整型u8a转换为数字字符 s8a + '0' // 符合:有符号整型s8a转换为数字字符 cha - '0' // 符合:字符cha转换为序数 '0' - s8a // 符合:有符号整型-cha转换为数字字符 s16a - 'a' // 不符合 '0' + f32a // 不符合,f32a是浮点型 cha + ':' // 不符合 cha - ena // 不符合 Rule 10.3 The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category

要求。表达式的值不应赋值给本质类型较窄 或 不同的基本类型 的对象。

宽数字赋值给窄数字,会发生截断。

Rule 10.4 Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category

要求。通常的算术转换时的运算符的2个操作数,应具有相同点基本类型类别。

Rule 10.5 The value of an expression should not be cast to an inappropriate essential type

建议。表达式的值不应转型为不恰当的本质类型。

Rule 10.6 The value of a composite expression shall not be assigned to an object with wider essential type

要求。符合表达式的值不应赋值给具有更宽基本类型的对象。

可以让运算过程提升数值宽度,但不要在赋值时提升。

// 符合 u16c = u16a + u16b; /* Same essential type */ u32a = ( uint32_t ) u16a + u16b; /* Cast causes addition in uint32_t */ // 不符合 u32a = u16a + u16b; /* Implicit conversion on assignment */ use_uint32 ( u16a + u16b ); /* Implicit conversion of fn argument */ Rule 10.7 If a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed then the other operand shall not have wider essential type

要求。如果一个复合表达式被用作执行常用算术转换的运算符的一个操作数,另一个运算数不应具有更宽的本质类型。

例子,

u32a * u16a + u16b /* No comp osite conversion */ ( u32a * u16a ) + u16b /* No composite conversion */ u32a * ( ( uint32_t ) u16a + u16b ) /* Both operands of * have same essential type */ u32a += ( u32b + u16b ) /* No composite conversion */ u32a * ( u16a + u16b ) /* Implicit conversion of ( u16a + u16b ) */ u32a += ( u16a + u16b ) /* Implicit conversion of ( u16a + u16b ) */ Rule 10.8 The value of a composite expression shall not be cast to a different essential type category or a wider essential type

要求。复合表达式的值不应转型为不同本质类型类别或一更宽的本质类型。

指针类型转换(Pointer type conversions) Rule 11.1 Conversions shall not be performed between a pointer to a function and any other type

要求。转换不应在函数指针和其他类型指针之间进行。

Rule 11.2 Conversions shall not be performed between a pointer to incomplete and any other type

要求。转换不应在不完整类型和其他类型指针之间进行。

不完整类型指: 1)void类型 2)未知大小数组 3)具有不完整类型元素的数组 4)未定义的结构体,联合体,或枚举(只有声明) 5)指向已声明但未定义的类的指针 6)声明但未定义的类

异常情况: 1)null指针产常量能转换为指向不完整类型的指针。 2)指向不完整类型的指针,能转换为void。

Rule 11.3 A cast shall not be performed between a pointer to object type and a pointer to a different object type

要求。转型不应在指向不同对象类型的指针之间进行。

例如,

uint8_t *p1; uint32_t *p2; /* Non-compliant - possible incompatible alignment */ p2 = ( uint32_t * ) p1; // 不符合 extern uint32_t read_value ( void ); extern void print ( uint32_t n ); void f ( void ) { uint32_t u = read_value ( ); uint16_t *hi_p = ( uint16_t * ) &u; /* Non-compliant even though probably correctly aligned */ // 不符合 *hi_p = 0; /* Attempt to clear high 16-bits on big-endian machine */ print ( u ); /* Line above may appear not to have been performed */ } Rule 11.4 A conversion should not be performed between a pointer to object and an integer type

建议。转型不应在指向对象的指针和整型之间进行。

Rule 11.5 A conversion should not be performed from pointer to void into pointer to object

建议。不应将指向void的指针转型为指向对象的指针。

反过来可以:能将指向对象的指针转型为void指针。

Rule 11.6 A cast shall not be performed between pointer to void and an arithmetic type

要求。转型不应在void指针和算术类型之间进行。

例如,

void * p; uint32_t u; /* Non-compliant - implementation-defined */ p = ( void * ) 0x1234u; /* Non-compliant - undefined */ p = ( void * ) 1024.0f; /* Non-compliant - implementation-defined */ u = ( uint32_t ) p; Rule 11.7 A cast shall not be performed between pointer to object and a non-integer arithmetic type

要求。转型不应在指向对象的指针和非整型算术类型之间进行。

Rule 11.8 A cast shall not remove any const or volatile qualification from the type pointed to by a pointer

要求。转型不应移除来自指针指向的类型的任何const 或 volatile限定符。

Rule 11.9 The macro NULL shall be the only permitted form of integer null pointer constant

要求。NULL宏是唯一允许的整型空指针常量的形式。

表达式(Expressions) Rule 12.1 The precedence of operators within expressions should be made explicit

建议。应明确表达式中运算符的优先级。

例如,

a[ i ]->n; /* Compliant - no need to write ( a[ i ] )->n */ *p++; /* Compliant - no need to write *( p++ ) */ sizeof x + y; /* Non-compliant - write either sizeof ( x ) + y * or sizeof ( x + y ) */ Rule 12.2 The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand

要求。移位运算符的右手操作数应在0~1之间,比左操作数的基本类型的位宽小。

Rule 12.3 The comma operator should not be used

建议。逗号操作符不应使用。

Rule 12.4 Evaluation of constant expressions should not lead to unsigned integer wrap-around

建议。常量表达式的值计算不应导致无符号整型环绕(溢出)。

无符号整型表达式没有严格的的溢出,取而代之的是环绕(wrap-around)。

例如,下面BASE所指数可能是16bit:

#define BASE 65024u switch ( x ) { case BASE + 0u: // 符合 f ( ); break; case BASE + 1u: // 符合 g ( ); break; case BASE + 512u: // 不符合,环绕到0 h ( ); break; } 副作用(Side effects) Rule 13.1 Initialiser lists shall not contain persistent side effects

要求。初始化列表不应包含持久副作用。

C90限制聚合类型的初始化器仅包含常量。但C99允许初值包含运行时计算的表达式,也允许作为匿名初始化对象的复合文字。初始化器列表中表达式求值过程中,副作用发生的顺序锁未指定的,因此,如果这些副作用持续存在,那么初始化的行为不可预测。

例如,

volatile u int16_t v1; void f ( void ) { /* Non-compliant - volatile access is persistent side effect */ uint16_t a[ 2 ] = { v1, 0 }; } void g ( uint16_t x, uint16_t y ) { /* Compliant - no side effects */ uint16_t a[ 2 ] = { x + y, x - y }; } uint16_t x = 0u; extern void p ( uint16_t a[ 2 ] ); void h ( void ) { /* Non-compliant - two side effects */ p ( ( uint16_t[ 2 ] ) { x++, x++ } ); } Rule 13.2 The value of an expression and its persistent side effects shall be the same under all permitted evaluation orders

要求。表达式的值和它的持久副作用,应在所有允许的计算顺序情况下相同。

Rule 13.3 A full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operator

建议。保护自增或自减运算符的完整表达式,除了由自增或自减运算符引起的副作用外,不应有其他潜在的副作用。

Rule 13.4 The result of an assignment operator should not be used

建议。赋值运算符的结果不应使用。

Rule 13.5 The right hand operand of a logical && or || operator shall not contain persistent side effects

要求。逻辑运算符 && 或 || 运算符不应包含持久副作用。

Rule 13.6 The operand of the sizeof operator shall not contain any expression which has potential side effects

强制。sizeof运算符的操作数不应包含任何存在潜在副作用的表达式。

控制状态表达式(Control statement expressions) Rule 14.1 A loop counter shall not have essentially floating type

要求。循环计数器不应有实质上的浮点类型。

Rule 14.2 A for loop shall be well-formed

要求。一个循环应有良好的结构。

例如,

// 下面loop循环计数器i和控制标记flag控制循环 bool_t flag = false; for ( int16_t i = 0; ( i < 5 ) && !flag; i++ ) { if ( C ) { flag = true; /* Compliant - allows early termination * of loop */ } i = i + 3; /* Non-compliant - altering the loop * counter */ } Rule 14.3 Controlling expressions shall not be invariant

要求。控制表达式不应是不变的。

该规则适用于: if/while/for/do..while/switch的条件; ?:运算符的第一个操作数。

例外情况: 1)无限循环。 2)do..while 循环的控制表达式允许为0。

Rule 14.4 The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type

要求。if语句的控制表达式和迭代语句的控制表达式,应具有本质上布尔类型。

控制流(Control flow) Rule 15.1 The goto statement should not be used

建议。goto语句不应使用。

Rule 15.2 The goto statement shall jump to a label declared later in the same function

要求。goto语句应跳转到同一函数内、goto语句后声明的label。

Rule 15.3 Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement

要求。goto语句引用的任何标签,应在相同块中声明,或者任何块中声明围住goto语句。

Rule 15.4 There should be no more than one break or goto statement used to terminate any iteration statement

建议。不应有超过1个break或goto语句终止任意迭代语句。

Rule 15.5 A function should have a single point of exit at the end

建议。函数应在末尾有单一退出点。

意思是说,每个函数只能有一个return语句。

Rule 15.6 The body of an iteration-statement or a selectionstatement shall be a compound statement

要求。迭代语句或选择语句的本体应为复合语句。

复合语句称为语句块,使用大括号包裹。该条规则意思是循环语句和if语句本体,需要使用"{}"包裹。除了一种特殊情况:if语句后马上跟着else。

Rule 15.7 All if . . else if constructs shall be terminated with an else statement

要求。所有if..else,都应该以else终止。

除非没有else,只要有else,最终必须以else终止,处理其他情形。

if ( flag_1 ) { action_1 ( ); } else if ( flag_2 ) { action_2 ( ); } // 不符合, 可以添加下面终止else使其符合该条规则 else { ; /* No action required - ; is optional */ } switch语句(Switch statements) Rule 16.1 All switch statements shall be well-formed

要求。所有switch语句应具有良好形式。

所有case + default。

Rule 16.2 A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement

要求。switch label只应用在封闭的复合语句是switch语句的主体的时候。

label是指case, default label。本条规则意思是,label只能出现在switch主体复合语句的最外层,不能穿插在其他复合语句内。 例如,

switch ( x ) { case 1: /* Compliant */ if ( flag ) { case 2: /* Non-compliant */ x = 1; } reak; default: break; } Rule 16.3 An unconditional break statement shall terminate every switch-clause

要求。无条件的break语句应终止每个子句。

例外情况:控制流跳转到下一个switch子句。例如,

switch ( x ) { case 0: break; /* Compliant - unconditional break */ case 1: /* Compliant - empty fall through allows a group */ case 2: break; /* Compliant */ case 4: a = b; /* Non-compliant - break omitted */ case 5: if ( a == b ) { ++a; break; /* Non-compliant - conditional break */ } default: ; /* Non-compliant - default must also have a break */ } Rule 16.4 Every switch statement shall have a default label

要求。每个switch语句应有default标签。

Rule 16.5 A default label shall appear as either the first or the last switch label of a switch statement

要求。默认标签应座位第一个或最后一个switch标签出现。

Rule 16.6 Every switch statement shall have at least two switch-clauses

要求。每个switch语句以u应有至少2个switch子句。

Rule 16.7 A switch-expression shall not have essentially Boolean type

要求。switch表达式不应具有本质上Boolean类型。

函数(Functions) Rule 17.1 The features of shall not be used

要求。的功能不应使用。

Rule 17.2 Functions shall not call themselves, either directly or indirectly

要求。函数不应直接或间接地调用自己。

Rule 17.3 A function shall not be declared implicitly

强制。函数不应隐式声明。

Rule 17.4 All exit paths from a function with non-void return type shall have an explicit return statement with an expression

强制。具有非void返回类型的函数的所有退出路径,应具有显式返回语句。

Rule 17.5 The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements

建议。与被声明为拥有数组类型的参数对应的函数参数,应具有适当的元素数量。

也就是说,实参数组元素数量,必须与函数参数的数组元素数量相匹配。

Rule 17.6 The declaration of an array parameter shall not contain the static keyword between the [ ]

强制。数组参数的声明,不应在[ ]中间包含static关键字的参数。

Rule 17.7 The value returned by a function having non-void return type shall be used

要求。非void返回类型的函数,必须返回值。

Rule 17.8 A function parameter should not be modified

建议。函数参数不应修改。

函数参数具有自动存储周期,C语言允许修改参数,但这么做会让人迷惑,而且与程序员期望冲突。不熟悉C的程序员可能会修改参数,而期望修改实参。

指针和数组(Pointers and arrays) Rule 18.1 A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand

要求。指针操作数的算术运算产生的指针,应寻址与该指针操作数相同的数组元素。

也就是说,指针指向一个数组,与整型数进行算术运算,得到的结果是另一个数组元素地址。

Rule 18.2 Subtraction between pointers shall only be applied to pointers that address elements of the same array

要求。指针之间的减法,应当仅仅用于寻址相同数组的指针。

Rule 18.3 The relational operators >, >=, < and , >=, 和 len * sizeof ( uint32_t ) ) ); *s2 = *s1; /* Only copies s1->len */ return s2; } Rule 18.8 Variable-length array types shall not be used

要求。可变长度数组类型不应使用。

例如,

void f ( int16_t n ) { uint16_t vla[ n ]; /* Non-compliant - Undefined if n


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3