网站掉权重是怎么回事,做公装的什么网站好,小程序开发平台哪家比较不错,厦门建设局耿家强文章目录 1、前言2、简介2.1、如何看待MISRA-C 20122.2、准则(guidelines)里面的指示(Directive)和规则(Rule)2.3、准则(guidelines)的级别(Category) 3、若干重要的Directive和Rule3.1、指示(Directive)Dir 2.1#xff08;必要#xff09; 所有的源文件编译过程不得有编译错… 文章目录 1、前言2、简介2.1、如何看待MISRA-C 20122.2、准则(guidelines)里面的指示(Directive)和规则(Rule)2.3、准则(guidelines)的级别(Category) 3、若干重要的Directive和Rule3.1、指示(Directive)Dir 2.1必要 所有的源文件编译过程不得有编译错误Dir 4.4建议不应该注释掉代码Dir 4.11必要检查传递给库函数参数值的有效性 3.2、规则(Rule)Rule 2.1必要不能含有不可达代码Rule 2.2必要不能含有死代码Rule 2.3~2.7建议不应该含有未使用的类型声明、标签、宏、形参Rule 5.1~5.9必要宏、类型定义/声明、函数定义/声明、变量定义/声明都不得重名Rule 5.3必要内部作用域声明的标识符不得隐藏外部作用域声明的标识符Rule 7.2必要无符号整形常量都是用u或者U后缀Rule 7.3必要小写字母l不能作为常量后缀Rule 7.4必要不能把字符串常量赋值给对象除非对象类型为const char*Rule 8.4必要具备外部链接的标识符必须有显式可见的声明Rule 8.5必要具备外部链接的标识符只能在一个文件声明一次Rule 8.8必要具备内部链接的变量和函数都必须使用static修饰Rule 8.11建议具备外部链接的数组声明应该显式指定长度Rule 9.1强制具备自动储存周期的对象在设定它的值之前不能读取Rule 10.3必要表达式的值不得赋给更窄的基本类型也不得赋给不同的基本类型类别Rule 11.9必要宏NULL必须为整数类型空指针常量的唯⼀允许形式Rule 12.1建议应明确表达式中操作数的优先级Rule 12.3建议不得使用逗号表达式Rule 12.4建议对常量表达式进⾏求值不应导致整数回绕Rule 15.4建议对于任何迭代语句最多只应使⽤⼀个break或goto语句进⾏终⽌Rule 15.5建议函数结尾应只有⼀个退出点Rule 15.7必要所有if … else if构造都必须以⼀个else语句终⽌Rule 16.4必要每个switch语句都必须有default标记Rule 17.2必要不得使用递归调用Rule 17.8建议不应该修改函数的形参Rule 21.3~21.11必要不得使用下面标准库中的接口 推荐的个人网站MISRA-C中文翻译但是它少了Directives部分Rule部分是比较详细的 推荐的MISRA-C条目整理文档百度云盘NaiveSystems Analyze 2022.1 Tech Spec CN 提取码fau8
1、前言 首先MISRA-C是一个C语言标准是一个文档是收费的所以网上的解读以及翻译都是有人在官网买过然后根据官网的文档而来 其次为什么要搞浓缩版因为MISRA-C实在太长了而且很多东西我们极其少用到比如初始化数组:
uint8_t array[2] {[0] 1, [1] 2}或者已经是默认行为如禁止使用goto等所以小白在这边精炼一些对实际项目和工作有帮助的内容进行展开
2、简介
2.1、如何看待MISRA-C 2012 MISRA-C 2012是属于第三版的MISRA-C一开始是为了汽车行业而专门定的C语言编程规范不包含代码风格说白了就是以前从事汽车行业开发的大佬在积累N年的C语言编程经验后得出的宝典宝典中沉淀着他们实际项目中遇到的奇葩问题的预防措施就是只要你按照宝典的要求去写代码那么就会减少很多奇奇怪怪的问题把这些问题扼杀在摇篮当中而不是等问题出现后再这样去写代码。 其实每一个嵌入式公司基本都会有内部的编程规范这些规范更多是软件总监等级别的人编写的像华为的编程规范阿里巴巴的编程规范但是这些商业的编程规范都比较倾向于他们所属的业务具有领域属性很多规定都是为业务而生所以不必诧异为什么里面会规定这样的内容因为每一条内容的背后都会有血的教训。但是MISRA-C在第二版之后就不具备领域属性了而是面向所有领域跨行业的应用因此现在很多公司的C语言编程规范多多少少都带着MISRA-C的内容在里面因为MISRA-C道出了C语言编程本身的各种问题而不是某个领域的问题 所以你可以当MISRA-C是一个字典工具偶尔看一看或者编程的时候突然想起有相关的就查一下不必一直啃食因为很多内容如果你没经历过大概就是从左耳进右耳就出去了当你先过一遍脑子里大概知道有提及哪些内容然后实际真的遇到后你自然会回来查阅的
2.2、准则(guidelines)里面的指示(Directive)和规则(Rule) 话不多说MISRA-C原文档的各种总览、背景、工具等我都忽略过直接进入主题MISRA-C既然是一个编程规范里面肯定是各种条例规定理所当然最重要的内容叫准则(guidelines)可以理解为最顶层的规范它由2部分组成 Directive翻译为指示引导你需要做这个事情但是这个事情没有判断对错的标准。实际项目是否符合规范是需要借助其它条件来证实的单纯依赖MISAR-C是不能证实的。比如Dir 4.3提及汇编语言必须被封装并隔离但是封装和隔离都是每一个项目根据实际情况来进行的接口不统一、做法不统一统一的只有这个思维这就是Directive Rule翻译为规则明确代码就是要这样做否则就违反标准。它不需要根据其它情况可以进行判定比如Rule 15.1 说明不应该使用goto语句你用了而且没有其它预防措施就是不符合MISRA-C规范没有用或者用了但是有各种预防措施就符合这一条的规范这就是Rule
2.3、准则(guidelines)的级别(Category) 其中每一条Dir或者Rule都有3种类型的级别 mandatory强制不允许违反 required必要只有在明确限制、要求和预防措施的情况下才可以违反 advisory建议在合理可行的范围内建议遵守 这3个级别是相同重要的差异点在于是否允许偏离标准偏离标准的例子如下为了某些特殊情况偏离标准比如将 int 类型值强制转为指针来实现访问内存地址空间映射的 I/O需要专门的文档记录这种违反MISRA-C的地方
// 内存中的0x0002地址内数据映射了某一I/O端口数据
#define PORT (*(volatile unsigned char *)0x0002)
// 修改该位置数据就相当于修改了该I/O端口数据
PORT 0x10u;3、若干重要的Directive和Rule Directive和Rule加起来会有170条每一条在官网文档种都有描述但是我们这里只选择若干重点的内容来展开这些都是养成良好编程习惯的重要规范对工作和实际项目有好处无坏处
3.1、指示(Directive)
Dir 2.1必要 所有的源文件编译过程不得有编译错误 小白理解每一次编译成功后都需要保持项目的 0 warning0 error 很多人会容忍warning的存在但这是不对的可能项目存在定义但未使用的代码然后触发warning虽然无关紧要但是太多的时候会把真正的warning掩盖掉比如if (x y)这种写法会触发warning如果你的项目本来就有几十个warning的时候那么多这1个也很难去看到于是你就错过了编译器帮你排查BUG的机会当出现问题的时候需要花费额外的时间去排查。因此永远遵守0 warning0 error能让编译器帮你识别很多问题
Dir 4.4建议不应该注释掉代码 小白理解注释代码容易和真正的注释产生混淆如果真的想暂时屏蔽一段代码应该要明确标注出起点和终点使用#if或者#ifdef起点然后#endif重点但是我觉得最好的做法是删掉他们利用git等代码管理工具来保存记录 很多人在实际项目中暂时取消某个功能就是使用注释因为注释一遍都是有快捷键的很方便就能选中代码然后注释掉但是由于真正的注释和这种行为的注释用法是一样的会让其他人看这个代码产生混淆它到底有没有作用如果真的存在一个能符合语法的注释它真的是一个注释而且混淆在这些屏蔽代码里面那么程序员将很难分清代码行为。
void FuncA(void)
{uint8_t Cnt 0;//符合Dir 4.4写法
#if 0Cnt;
#endif//不符合Dir 4.4写法
// Cnt;
}Dir 4.11必要检查传递给库函数参数值的有效性 小白理解在调用库函数之前必须先检查参数是否有效再进行调用而不是相信库函数会自己检查原则上就是不相信其它库的接口在遇到非法数据时行为是正常的都要假设是异常的。 其实这种思维在Linux早已根深蒂固Linux内核的设计思维之一就是不相信用户会正确使用内核函数因此给用户加了各种权限让用户需要通过各种验证后才能使用接口所有我们在调用别人的库之前就需要检查入口同时编写接口的时候也要做到入口参数的检查。
/* A.h */
extern void FuncA(uint8_t* pMsg);
extern uint8_t* GetMsgPointer(void);/* B.c */
void FuncB(void)
{uint8_t* pM GetMsgPointer();//符合Dir 4.11写法if (NULL ! pM) {FuncA(pM);}//不符合Dir 4.11写法FuncA(pM);
}3.2、规则(Rule)
Rule 2.1必要不能含有不可达代码 小白理解原因是1这些代码占用Ram和Flash而且毫无意义纯属浪费2是可能导致编译器产生一些又臭又长的跳转指令但实际并不需要3是可能导致整个循环变慢。 很多不可达的代码都有warning请遵守Dir 2.1保持0 warning0 error。另外就是有比较难检查到的不可达代码就比较依赖静态检查工具来进行了但是一般遵守if-else或者switch-case的时候不要提前return而且确保判断的值范围都在内部一般不会有什么大问题。
#include stdafx.h
typedef enum ErrStatus {Success 0,Err_1,Err_2
} ErrStatus;ErrStatus f(int x) {if (x 0) {return Err_1;}else{return Success;}
}int main()
{ErrStatus x f(5);switch (x){case Err_1:printf(err 1); break;case Err_2:printf(err 2 ); break; /* unreachable code */default:printf(Success); break;}
}Rule 2.2必要不能含有死代码 小白理解把调试代码和取消的功能删干净再上传业务代码。 意在提醒实际项目中调试或者删除功能时要把代码弄干净写了一个debug接口结束后忘记删除然后停留在业务上面如果不调试它是没有任何作用的占用Ram和Flash纯属浪费还容易引起胡混淆
extern volatile uint16_t v;
extern char *p;
void f(void)
{uint16_t x;(void)v; /* Compliant - 这种方式用于抑制编译器的未使用告警是有意义的如果删除就会产生编译器告警不视为dead code */(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 */
}Rule 2.3~2.7建议不应该含有未使用的类型声明、标签、宏、形参 小白理解我觉得这一堆跟Rule 2.2是一样的含义没用的代码弄干净点我推荐直接删掉连#if和#ifdef都不要用除非是必须的调试代码。 Rule 5.1~5.9必要宏、类型定义/声明、函数定义/声明、变量定义/声明都不得重名 小白理解在 C99 中规定外部链接标识符的有效识别长度为 31 个字符(是否大小写敏感取决于编译器)也就是前 31 个字符需要唯一才能区分两个外部链接标识符表示不同的项。 我建议每一个对象的命名都跟随其所属模块作为前缀这样重名的概率低很多比如EEPROM模块那么里面的函数、变量、struct、enum等都是EEPROM_作为前缀这样即使另一个模块比如Timer也有一个名字教Data的那么EEPROM_Data就和Timer_Data不相同另外是命名不要过长模块_动作_名称控制在31字符内
/* 1234567890123456789012345678901********* Characters */
int32_t engine_exhaust_gas_temperature_raw;
int32_t engine_exhaust_gas_temperature_scaled; /* Non-compliant,两个变量名的前31个字符相同 */
/* 1234567890123456789012345678901********* Characters */
int32_t engine_exhaust_gas_temp_raw;
int32_t engine_exhaust_gas_temp_scaled; /* Compliant */Rule 5.3必要内部作用域声明的标识符不得隐藏外部作用域声明的标识符 小白理解函数内部的局部变量不要和全局变量重名 特别是循环计数变量习惯用i,j,k没问题但是一个函数内部每一个循环的计数名字都要不一样可以有i,j,k,m,n,z,y……都行最好就是明确当前循环计数的含义比如当前循环是找A的计数值变量就叫A_i或者其它具体含义下一个循环是找B的就叫B_i或者其它具体含义只有一个循环直接叫i或者其它具体含义
void fn1(void)
{int16_t i; /* Declare an object i */{int16_t i; /* Non-compliant - hides previous i 第三种情况连续嵌套块*/i 3; /* Could be confusing as to which i this refers */}
}
struct astruct
{int16_t m;
};
extern void g(struct astruct *p);
int16_t xyz 0; /* Declare an object xyz */
void fn2(struct astruct xyz) /* Non-compliant - outer xyz is* now hidden by parameter name */
{g(xyz);
}
uint16_t speed;
void fn3(void)
{typedef float32_t speed; /* Non-compliant - type hides object */
}Rule 7.2必要无符号整形常量都是用u或者U后缀 小白理解不要相信编译器可以很好帮你处理常量类型 当你的的写法不一样的时候4095是uint16_t或者uint32_t或者sint32_t都是不知道的但是你只要写成4095u那他无论怎么都是无符号型而且必须是uint16_t起步这样子就算和有符号的进行计算也不会认为最高位是符号位了 例如整数常量 40000 在 32 位环境中属于带符号 int 类型但在 16 位环境中属于 signed long 类型。数值 0x8000 在 16 位环境中属于 unsigned int 类型但在 32 位环境中属于 signed int 类型。在 2-bit int 和 64-bit long 环境中
void R_7_2(void)
{use_int32(2147483647); /* int constant */use_int32(0x7FFFFFFF); /* int constant */use_int64(2147483648); /* long constant */use_uint32(2147483648U); /* unsigned int constant */use_uint32(0x80000000); /* unsigned int constant - Non-compliant */use_uint32(0x80000000U); /* unsigned int constant */
}Rule 7.3必要小写字母l不能作为常量后缀 小白理解纯属和数字1长得很像肉眼难以区分干脆不用 const int64_t a 0L;
const int64_t b 0l; /* Non-compliant */
const uint64_t c 0Lu;
const uint64_t d 0lU; /* Non-compliant */
const uint128_t e 0ULL;
const uint128_t f 0Ull; /* Non-compliant */
const int128_t g 0LL;
const int128_t h 0ll; /* Non-compliant */
const float128_t m 1.2L;
const float128_t n 2.4l; /* Non-compliant */
Rule 7.4必要不能把字符串常量赋值给对象除非对象类型为const char* 小白理解道理很简单大家都懂字符串常量不可被改变但是写接口的时候很多会忽略只能说带来隐患写上就100%没问题 extern void f1(char *s1);extern void f2(const char *s2);static void g2(void)
{f1(string); /* Non-compliant形参为非const实参是字符串常量 */f2(string); /* Compliant */
}static char *name1(void)
{return (MISRA); /* Non-compliant返回参数类型非const */
}static const char *name2(void)
{return (MISRA); /* Compliant*/
}void R_7_4(void)
{char *s string; /* Non-compliant */const volatile char *p string; /* Compliant */0123456789[0] *; /* Non-compliant未定义行为 */g2();(void)name1();(void)name2();use_const_char_ptr(s);use_const_volatile_char_ptr(p);
}Rule 8.4必要具备外部链接的标识符必须有显式可见的声明 小白理解说白了就是调用文件以外的变量、函数、宏等都需要有显式的声明而且变量和函数都必须带extern extern void func1(void);
extern void func2(int16_t x, int16_t y);
extern void func3(int16_t x, int16_t y);
void func1(void)
{/* Compliant */
}
void func2(int16_t x, int16_t y)
{/* Compliant */
}
void func3(int16_t x, uint16_t y)
{/* Non-compliant - parameter types different违反规则8.3 */
}
void func4(void)
{/* Non-compliant - no declaration of func4 before this definition */
}
static void func5(void)
{/* Compliant - rule does not apply to objects/functions with internal* linkage */
}
Rule 8.5必要具备外部链接的标识符只能在一个文件声明一次 小白理解声明都放在.h里面不要越界操作 这个是和Rule 8.4有关系的Rule 8.4是说外部链接的标识需要有声明这里则说只能声明一次稍微转化一下就是规定外部链接的标识符只能在.h里面声明且只有1次声明换言之不要在.c文件里面用extern把其它文件的变量或者函数给包含进来这种属于不合规的做法正常使用你只能#include一个.h头文件进来如果只是临时调试使用调试结束后记得删干净。
/* featureX.h */
extern int16_t a; /* Declare a *//* file.c */
#include featureX.hint16_t a 0; /* Define a */Rule 8.8必要具备内部链接的变量和函数都必须使用static修饰 小白理解不给别人用的变量和函数都加上static修饰 1来可以防止重名问题2来可以清晰阅读一个.c里面哪些只限于内部使用3来添加权限属性别人在.c里面强制extern你的static变量或者函数是会编译失败的他必须再去你文件里面删掉static也就是让越界行为变得复杂稍微再防止一下
static int32_t x 0; /* definition: internal linkage */
extern int32_t x; /* Non-compliant,先前已存在x的声明* 导致这个x的链接性就是内部的* 而不是我们平常认为的用extern修饰的是外部链接 */
static int32_t f(void); /* declaration: internal linkage */
int32_t f(void) /* Non-compliant */
{return 1;
}
static int32_t g(void); /* declaration: internal linkage */
extern int32_t g(void) /* Non-compliant */
{return 1;
}Rule 8.11建议具备外部链接的数组声明应该显式指定长度 小白理解不要用extern uint8_t array[]一定要用#define LENGTH_MAX 10 extern uint8_t array[LENGTH_MAX] 没什么好说的给被人用的数组一定要让别人有方法知道长度否则就存在越界的隐患你可以用函数返回可以用全局变量可以用宏可以用枚举等等一定要用其中一个
extern int32_t array1[10]; /* Compliant */
extern int32_t array2[]; /* Non-compliant */Rule 9.1强制具备自动储存周期的对象在设定它的值之前不能读取 小白理解局部变量定义时即初始化不要偷懒 这也是减少隐患的预防性编程定义局部变量的时候顺手写一个赋值操作这带来了确定性天晓得下一个人会不会在局部变量还没赋值的时候使用或者你不会但下一个维护你代码的人会
static void f(bool_t b, uint16_t *p)
{if (b){*p 3U;}
}static void g(void)
{uint16_t u; /* Non-compliant declarationu未被显式赋值 */f(false, u);if (u 3U) /* Non-compliant use - u has not been assigned a value. */{use_uint16(u); /* */}
}static void jmp_over_init(void)
{goto L1; /* violates R.15.1 */uint16_t x 10u;
L1:// 此处的x声明虽然被跳过但x还是被正常声明了可编译通过x x 1u; /* Non-compliant - x has not been been assigned a value */use_uint16(x);
}void R_9_1(void)
{bool_t b get_bool();uint16_t val 3u;f(b, val);use_uint16(val);g();jmp_over_init();
}Rule 10.3必要表达式的值不得赋给更窄的基本类型也不得赋给不同的基本类型类别 小白理解运算类型始终保持一致性多使用强制类型转换 这是避免编译器的隐形转换导致数据截断或者精度问题编译器不一定会按照你的想法进行隐式转换自己强制转换能掌控全局
u8a 2; /* Compliant By 例外1 */
u8a 2 * 24; /* Compliant By 例外1 */uint8_t u8f 1.0f; /* Non-compliant - unsigned and floating */
bool_t bla 0; /* Non-compliant - boolean and signed不符合例外1因为bla不是无符号整型 */
cha 7; /* Non-compliant - character and signed */
u8a a; /* Non-compliant - unsigned and character */
u8b 1 - 2; /* Non-compliant - unsigned and signed不符合例外1因为1-2不是非负的 */
u8c a; /* Non-compliant - u8c u8c a assigns character to unsigned */s8a K2; /* Non-compliant - Constant value does not fit */
u16a u32a; /* Non-compliant - uint32_t to uint16_t */s8a -123L; /* Non-compliant - signed long to int8_t */u8a 6L; /* Non-compliant - signed long to uint8_t不符合例外1 *//* Standard Type has rank greater than int,* so exception does not apply *//* integer constant expression from with value 5U and UTLR of unsigned char */
u8a (uint16_t)2U (uint16_t)3U; /* Compliant例外1 *//* integer constant expression from with value 100000U and UTLR of unsigned int */
u16a (uint16_t)50000U (uint16_t)50000U; /* Non-compliant不符合例外1超过了u16的最大值 *//* Top-level cast returns C standard type of unsigned short */
u8a (uint16_t)(2U 3U); /* Non-compliant经过转换后不再是常量表达式不符合例外1 */Rule 11.9必要宏NULL必须为整数类型空指针常量的唯⼀允许形式 小白理解只能使用NULL来判断空指针不能使用0 这个网上有很多解释了(void*)0和0的含义是不一样的你不能相信编译器会把他们认为是一样所以统一使用NULL的(void*)0
#define MY_NULL_1 0
#define MY_NULL_2 (void *)0
#define MY_NULL_3 NULLextern void f9(uint8_t *p);int32_t *p1 0; /* Non-compliant */
int32_t *p2 (void *)0; /* Compliant */
int32_t *p3 MY_NULL_3; /* Compliant */if (p1 MY_NULL_1) /* Non-compliant - also breaks R.14.3 */
{
}
if (p2 MY_NULL_2) /* Compliant - but breaks R.14.3 */
{
}f9(NULL); /* Compliant for any conforming definition of* NULL, such as:* 0* (void *)0* (((0)))* (((1 - 1)))*/Rule 12.1建议应明确表达式中操作数的优先级 小白理解不要怜惜括号的使用每一层计算都需要加 预防性编程的一种把结果掌控在程序员手里而不是选择相信编译器比如很多人喜欢这样写if ( a b c d)这完全没问题但是MISRA-C更加推荐你这样写if ( (a b) (c d))给每一个运算都加上括号维护起来这是100%不会出现符号优先级问题的
Rule 12.3建议不得使用逗号表达式 小白理解很少人用优先级和副作用问题多干脆不要用 逗号表达式最多出现在for的子句里面比如for(i 0, j 0; ; i, j)这样子看起来是完全没问题的如果只是如此简单不过如果你的计算稍微复杂比如突然有一天j要从i开始就改成了for(i 0, j i; ; i, j)请问如果你选择相信编译器j的值是0还是没有赋值0之前的i值还是那句话不要依赖编译器能自己完成的事情就不要让编译器去做取消逗号表达式的使用
/* also violates R.14.2 */
for (i 0, p a[0]; /* Non-compliant */i N;i, p) /* Non-compliant */
{
}Rule 12.4建议对常量表达式进⾏求值不应导致整数回绕 小白理解常量的加法不应该溢出这个是很难人工检查出来的一般依赖静态检查或者编程的时候注重常量的加法 // 与 case 标签关联的表达式必须是常量表达式。
// 如果在 case 表达式求值期间发生无符号环绕则很可能是无意的。
// 在具有 16 位 int 类型的计算机上会导致以下示例中的回绕
#define BASE 65024u
switch (x)
{
case BASE 0u:f();break;
case BASE 1u:g();break;
case BASE 512u: /* Non-compliant - wraps to 0 */h();break;
}Rule 15.4建议对于任何迭代语句最多只应使⽤⼀个break或goto语句进⾏终⽌ 小白理解禁止禁止禁止使用goto另外遵循单点退出原则并且退出点必须在模块的最后一句 单点退出原则也就是一个模块只能有一个退出点这个的好处是约束了模块的行为是稳定的它必然会跑到固定的退出点而退出点固定在模块最后一句意味着模块内每一句语句都会被执行防止了一些锁的成对操作缺漏或者模块的退出操作缺漏预防性编程
#define LIMIT 100u/* Note: All uses of goto also break R.15.1 */void R_15_4(void)
{uint32_t x;uint32_t y;uint32_t z;for (x 0; x LIMIT; x){if (ExitNow(x)){break; /* compliant - single exit from outer loop */}for (y 0; y x; y){if (ExitNow(LIMIT - y)){break; /* compliant - single exit from inner loop* 这个break仅用来退出本for循环而不是上级for,* 所有和上面那个break不冲突 */}}}for (x 0; x LIMIT; x){if (BreakNow(x)){break;}else if (GotoNow(x)){goto EXIT; /* Non-compliant - break and goto in loop */}else{KeepGoing(x);}}EXIT:;while (x ! 0u){if (x 1u){break;}while (y ! 0u){if (y 1u){// 这个goto直接退了两层while,和上面的break冲突goto L1; /* Non-compliant (outer loop) Compliant (inner loop) *//* goto causes early exit of both inner and outer loop */}}}
L1:z x y;
}Rule 15.5建议函数结尾应只有⼀个退出点 小白理解和Rule 15.4的一个意思遵守单点退出原则 static bool_t f(uint16_t n, const char *p)
{if (n MAX){return false; /* Non-compliant */}if (p NULL){return false; /* Non-compliant */}return true;
}Rule 15.7必要所有if … else if构造都必须以⼀个else语句终⽌ 小白理解强制让你思考else的内容也就是让你多思考一个分支情况防止发生错漏实际项目很多时候就是说”啊少考虑了这种情况“ if (flag_1)
{action_f1();
}
else if (flag_2)
{action_f2();
}
/* Non-compliant */if (flag_1)
{action_f1();
}
else if (flag_2)
{action_f2();
}
else
{; /* No action required - ; is optional */
}Rule 16.4必要每个switch语句都必须有default标记 小白理解switch或许有你考虑不到的值如果没有default则switch会退出你想debug的机会都难多写一个default里面加打印语句100%不会错漏 switch (x)
{case 0:x;break;case 1:case 2:break;/* Non-compliant - default label is required */
}Rule 17.2必要不得使用递归调用 小白理解嵌入式的RAM太宝贵了就算你有1M的RAM也经不住递归的堆栈开辟随时栈溢出的风险除非你有G级别的RAM但这已经不太属于嵌入式了不要用是100%不会出现递归导致的栈溢出问题 static uint16_t fn_a(uint16_t parama)
{uint16_t ret_val;if (parama 0U){ret_val parama * fn_a(parama - 1U); /* Non-compliant */}else{ret_val parama;}return ret_val;
}Rule 17.8建议不应该修改函数的形参 小白理解参数或许需要拿来判断原始值留住备份总是一个好习惯 int16_t glob 0;
void proc(int16_t para)
{para glob; /* Non-compliant */
}
void f(char *p, char *q)
{p q; /* Non-compliant */*p *q; /* Compliant */
}Rule 21.3~21.11必要不得使用下面标准库中的接口 小白理解不要相信标准库能很好兼容你的芯片直接不用是100%不出标准库问题 stdlib.h中的动态内存接口、sethmp.h所有接口、signal.h所有接口、stdlib的标准IO接口、atof、atoi、atoll、atol、system终止函数、bsearch、qsort、time、date、tgmath.h所有接口
#include stdlib.hvoid R_21_3(void)
{char_t *p;p (char_t *)malloc(11U); /* Non-compliant: use of malloc */if (p ! NULL){(void)realloc(p, 20U); /* Non-compliant: use of realloc */}free(p); /* Non-compliant: use of free */p (char_t *)calloc(10, sizeof(char_t)); /* Non-compliant: use of calloc */free(p); /* Non-compliant: use of free */
}