C语言编程规范这部分一直想总结一下。现在终于付诸行动了。
其实之前讲过一些面试题,参看:嵌入式面试知识点总结 – C语言篇
里面已经有包含一部分了,比如《高质量C++ C编程指南》.林锐着.pdf。
此次主要参考 华为技术有限公司c语言编程规范
和 MISRA C2012
再详细讲一下C语言的编程规范。
下载:编程规范
这篇文章主要以MISRA C 2012 中文版为基础,再将华为C语言编程规范融入其中。
看来之前总结两年的 C语言再学习 专栏,又有用武之地了。
参看:C语言再学习 – 声明与定义
声明一个变量只是将变量名标识符的有关信息告诉编译器,使编译器“认识”该标识符,但声明不一定引起内存的分配。而定义变量意味着给变量分配内存空间,用于存放对应类型的数据,变量名就是对相应的内存单元的命名。在C/++程序中,大多数情况下变量声明也就是变量定义,声明变量的同时也就完成了变量的定义,只有声明外部变量时例外。函数类似,声明只是告诉编译器有这个名称、类型的函数,而定义则是函数的真实实现。
简单一句话,定义创建了对象并为这个对象分配了内存,声明没有分配内存。
以下这些就是声明:
extern int bar;
extern int g(int, int);
double f(int, double); // 对于函数声明,extern关键字是可以省略的。
class foo; // 类的声明,前面是不能加class的。
与上面的声明相应的定义如下:
int bar;
int g(int lhs, int rhs) {
return lhs*rhs;}
double f(int i, double d) {
return i+d;}
class foo {
};
参看:C语言再学习 – 存储类、链接
分为三类,外部连接(链接)(external linkage)、内部连接(链接)(internal linkage)和无连接(链接)(no linkage)。
外部链接:
一个具有外部链接的变量可以在一个多文件程序的任何地方使用。
int n = 5; /*文件作用域,外部链接,未使用 static */
int main (void)
{
...
return 0;
}
内部链接:
一个具有内部链接的变量可以在一个文件的任何地方使用。
static int dodgers = 3; /*文件作用域,内部链接,使用了 static ,该文件所私有*/
int main (void)
{
...
return 0;
}
空链接:
具有代码块作用域或者函数原型作用域的变量有空链接,意味着它们是由其定义所在的代码块或函数原型所私有的。
double blocky (double cleo)
{
double patcrick = 0.0; /*代码块作用域,空链接,该代码块所私有*/
int i;
for (i = 0; i < 10; i++)
{
double q = cleo * i; /*q作用域的开始*/
...
patrick * = q;
} /*q作用域的结束*/
return patrick;
}
本规范的编制,具有普适性,故会出现如“对象”、“类”这些标准 C 中不提及的概念,对象在 C 语言中的直接对应是变量。当前对象不仅仅是变量,但本译文仅限考虑标准 C(准确的说是嵌入式 C),故不过多描述,我们将其当成“变量”理解即可。
每条 MISRA C 准则都可以被归类为“规则”或“指令”。
MISRA C2012将规则和指令均分为三个级别:
- 级别:必要
- 解读:程序应仅使用所选标准版本中指定的 C 语言及其库的功能
- 级别:建议
- 解读:不要用编程语言扩展属性,否则会降低程序的可移植性
- 级别:必要
- 解读:一些未定义或未指定的行为有特定的规则处理。此规则意在防止其他未定义和关键的未指定行为。MISRA C 的许多准则旨在避免某些未定义和未指定的行为。 例如,遵守 Rule 11.4、Rule 11.8 和 Rule 19.2 的所有内容可确保在
C 中不能创建指向使用 const 限定类型声明的对象的非 const 限定指针。这避免了 C90 [Undefined 39]和 C99
[Undefined 61]。
- 级别:必要
- 解读:如果一个程序没有表现出任何未定义的行为,那么无法到达的代码就不能被执行,也不能对程序的输出产生任何影响。因此,无法到达的代码的存在可能表明程序逻辑中的错误。
无法到达的代码会占用目标机器的内存空间,可能会导致编译器在围绕无法到达的代码传输控制时选择更长的、更慢的跳转指令。而且在循环中,它可以防止整个循环驻留在指令缓存中。
enum light {
red, amber, red_amber, green };
enum light next_light ( enum light c )
{
enum light res;
switch ( c )
{
case red:
res = red_amber;
break;
case red_amber:
res = green;
break;
case green:
res = amber;
break;
case amber:
res = red;
break;
default:
{
/* 当参数 c 的值不是枚举型 light 的成员时, 此 default 分支才可达 */
error_handler ( );
break;
}
}
return res;
res = c; /* 违规 - 此语句肯定不可达 */
}
- 级别:必要
- 解读:任何可以删除掉但是不影响程序正常运行的代码都是无效代码,由于无效代码可能被编译器删除,所以它的存在可能会引起混乱。
void g(void)
{
/* 合规 - 此函数中无任何操作 */
}
void h(void)
{
g(); /* 违规 - 该调用可以被移除 */
}
- 级别:建议
- 解读:如果声明了类型但没有使用,那么审阅者就不清楚该类型是冗余的还是错误地未使用。
int16_t unusedtype(void)
{
typedef int16_t local_Type; /* 违规 */
return 67;
}
- 级别:建议
- 解读:如果一个类型标签被声明但从未被使用过,对于审阅者来说,无法确定该类型标签是多余的还是被错 误闲置的。
typedef struct record_t /* 违规 */
{
uint16_t key;
uint16_t val;
} record1_t;
typedef struct /* 合规 */
{
uint16_t key;
uint16_t val;
} record2_t;
- 级别:建议
- 解读:如果一个宏被声明但从未被使用过,对于审阅者来说,无法确定该宏是多余的还是被错误闲置的。
void use_macro(void)
{
#define SIZE 4
#define DATA 3 /* 违规 - DATA 未被使用 */
use_int16(SIZE);
}
- 级别:建议
- 解读:如果一个执行标签(label)被声明但从未被使用过,对于审阅者来说,无法确定该执行标签是多余的还是被错误闲置的。
void unused_label(void)
{
int16_t x = 6;
label1: /* 违规 */
use_int16(x);
}
tag 和 label,两者翻译为中文都是标签,差别在于tag为枚举、结构体、联合体类型的标签,label为goto语句执行目的地的标签,本文中为区分,将它们分别描述为了类型标签与执行标签。
- 级别:建议
- 解读:绝大多数函数都将使用它们所定义的每一个参数。如果函数中的参数未被使用,则可能函数的实现与其预期定义不匹配。本规则强化描述了这一潜在的不匹配。
void withunusedpara(uint1 6_t *para1, int16_t unusedpara) /* 违规 - 参数未使用 */
{
*para1 = 42U;
}
- 级别:必要
- 解读:“/”和“//”均为注释起始的字符序列,如果在一段由“/”起始的注释中,又出现了“/”或“//”,那么很可能是由缺少“/”引起的。如果这两个注释起始的字符序列出现在由“//”起始的注释中,则很可能是因为使用“//”注释掉了代码。
x = y // /*
+ z
// */
;
此示例得出的结果是 x=y+z,但在没有两个“//”的情况下,结果是 x=y。
- 级别:必要
- 解读:如果包含“//”注释的源代码行在源字符集中以“\”字符结尾,则下一行将成为注释的一部分。 这可能会导致意外删除代码。
extern bool_t b;
void f(void)
{
uint16_t x = 0; // comment \
if (b)
{
++x; /* if 语句被作为注释处理, 这里无条件执行 */
}
}
**面试题:**以下注释哪条是错误的??
#include <stdio.h>
int main (void)
{
int /*...*/i;
char* s = "abcd //efg";
//hello \
world!
//in/*...*/t i;
return 0;
前三条注释都是对的,有没有想到。
- 级别:必要
- 解读:若八进制或十六进制转译序列后跟随其他字符,会造成混淆。例如,字符串“\x1f”仅由一个字符组成,而字符串“\x1g”则是由两个字符“\x1”和“g”组成。如果给字符常量或字符串文字中的每个八进制或十六进制转义序列增加显示的终止标识,则可以减少混淆的可能性。
const char *s1 = "\x41g"; /* 违规 - 无法区分哪个是转译序列, 哪个又是普通字符 */
const char *s2 = "\x41" "g"; /* 合规 - 以字符串结束标识转译序列的结束 */
const char *s3 = "\x41\x67"; /* 合规 - 以新的转译序列起始标识前一个转译序列的结束 */
int c1 = '\141t'; /* 违规 - 无法区分哪个是转译序列, 哪个又是普通字符 */
int c2 = '\141\t'; /* 合规 - 以新的转译序列起始标识前一个转译序列的结束 */
参看:C语言再学习 – 转义字符
建议,不使用八进制和十六进制转义序列。
- 级别:建议
- 解读:三字母词(或叫三联符序列)由两个问号起始,后跟一个特定字符组成。截至目前(2020 年),三字母词只有 9 个:
源代码中的“三字母词”,在编译阶段会被替换为“对应的字符”。 而它们会与两个问号的其他用法引起意外混淆。
"(Date should be in the form ??-??-??)"
会被编译器解析为
"(Date should be in the form ~~]"
参看:C语言再学习 – 三字母词(转)
下面是我们很容易犯的一个错误(摘自《C和指针》):
#include <stdio.h>
int main (void)
{
printf("??( \n");
printf("??) \n");
return 0;
}
root@# gcc test.c
test.c: 在函数‘main’中:
test.c:4:10: 警告: 三元符 ??( 被忽略,请使用 -trigraphs 来启用 [-Wtrigraphs]
test.c:5:10: 警告: 三元符 ??) 被忽略,请使用 -trigraphs 来启用 [-Wtrigraphs]
root@# gcc -trigraphs test.c
输出结果:
[
]
注意 :由于编译器的种类各样,对ANSI C的支持也不一样,所以可能会有些C编译器不处理“三字母词”,会将它们当做普通的字符串来处理。 以上测试是在VC++ 6.0下进行的,对于GCC编译器,需要在编译的时候添加选择"-ansi"或者"-trigraphs"。
- 级别:必要
- 解读:“不重名”取决于实现和所使用的 C 语言版本:在 C90 中,最小有效字符范围是前 6 个字符,且不区分大小写;在 C99 中,最小有效字符范围是前 31 个字符,而其通用字符和扩展字符的有效范围是 6 到 10 个字 符。
/* 1234567890123456789012345678901********* Characters */
int32_t engine_exhaust_gas_temperature_raw;
int32_t engine_exhaust_gas_temperature_scaled; /* 违规 */
/* 1234567890123456789012345678901********* Characters */
int32_t engine_exhaust_gas_temp_raw;
int32_t engine_exhaust_gas_temp_scaled; /* 合规 */
全局变量、宏、全局函数等,均需符合此准则,以 C99 为例,前 31 个字符必须不相同,一个有效的办法是,命名少于 31 个字符,且不重名。
- 级别:必要
- 解读:如果两个标识符都是外部标识符,则本准则不适用,因为此情况适用于 Rule 5.1。如果每个标识符都是宏标识符,则本准则不适用,因为这种情况已被 Rule 5.4 和 Rule 5.5 涵盖。 “不重名”的定义取决于实现和所使用的 C 语言版本:
◆ 在 C90 中,最低要求是前 31 个字符有效。
◆ 在 C99 中,最低要求是前63 个字符有效,通用字符或扩展源字符视为一个字符。
/* 1234567890123456789012345678901********* Characters */
extern int32_t engine_exhaust_gas_temperature_raw;
static int32_t engine_exhaust_gas_temperature_scaled; /* 违规 */
void f(void)
{
/* 1234567890123456789012345678901********* Characters */
int32_t engine_exhaust_gas_temperature_local; /* 合规 */
}
/* 1234567890123456789012345678901********* Characters */
static int32_t engine_exhaust_gas_temp_raw;
static int32_t engine_exhaust_gas_temp_scaled; /* 合规 */
- 级别:必要
- 解读:如果在内部作用域中声明一个标识符,与在外部作用域中已经存在的标识符重名,则最内部的声明将“隐藏”外部的声明。 这可能会导致开发人员混乱。
extern void g(struct astruct *p);
int16_t xyz = 0; /* 定义变量 "xyz" */
void fn2 (struct astruct xyz) /* 违规 - 外部定义的 "xyz" 被同名形参隐藏 */
{
g(&xyz);
}
uint16_t speed;
void fn3(void)
{
typedef float32_t speed; /* 违规 - 类型将变量给隐藏 */
}
参看:C语言再学习 – 存储类、链接
按照C语言作用域划分:
一个C变量的作用域可以是代码块作用域、函数原型作用域,或者文件作用域。
这跟上面的内部外部作用域也不同~~
- 级别:必要
- 解读:本准则要求在定义一个宏时,其命名必须不同于已定义的其他宏的名称,和已定义的参数的名称。它还要求给定宏的参数名称彼此不同,但不要求宏参数名称在两个不同的宏之间不同。
“不重名”的定义取决于实现和所使用的 C 语言版本:
◆ 在 C90 中,最低要求是宏标识符的前 31 个字符有效。
◆ 在 C99中,最低要求是宏标识符的前 63 个字符有效。
/* 1234567890123456789012345678901********* Characters */
#define engine_exhaust_gas_temperature_raw egt_r
#define engine_exhaust_gas_temperature_scaled egt_s /* 违规 */
/* 1234567890123456789012345678901********* Characters */
#define engine_exhaust_gas_temp_raw egt_r
#define engine_exhaust_gas_temp_scaled egt_s /* 合规 */
- 级别:必要
- 解读:宏名称和标识符保持不同有助于避免开发人员混淆。
在这里插入代码片
因为它后面没有“(”字符。因此,标识符在进行预处理后仍存在。#define Sum(x, y) ((x) + (y))
int16_t Sum; /* 违规 - 上面的宏Sum 与该变量重命名 */
- 级别:必要
- 解读:如果多个 typedef 名称命名相同而它们实际指代又是不同的函数、对象或枚举常量时,开发人员会被困扰。
void func ( void )
{
{
typedef unsigned char u8_t;
}
{
typedef unsigned char u8_t; /* 违规 - 重复使用 */
}
}
typedef float mass;
void func1 ( void )
{
float32_t mass = 0.0f; /* 违规 - 重复使用 */
}
typedef struct list
{
struct list *next;
uint16_t element;
} list; /* 合规 - 符合例外的情况 */
typedef struct
{
struct chain
{
struct chain *list;
uint16_t element;
} s1;
uint16_t length;
} chain; /* 违规 - 标记 "chain" 与 typedef 不关联 */
- 级别:必要
- 解读:重用标签(tag)名称可能会导致开发人员混乱。这里的标签tag为枚举、结构体、联合体类型的标签。
struct stag
{
uint16_t a;
uint16_t b;
};
struct stag a1 = {
0, 0 }; /* 合规 - 与前面的定义一致 */
union stag a2 = {
0, 0 }; /* 违规 - 与声明的 struct stag 不一致。
* 同时也违背了C99的约束 */
- 级别:必要
- 解读:用作外部标识符的标识符不得在任何命名空间或编译单元中用于任何其他目的,即使它没有链接的对象。
/* file1.c */
int32_t count; /* "count" 具有全局属性(全局变量) */
void foo ( void ) /* "foo" 具有全局属性(全局函数) */
{
int16_t index; /* "index" 无全局属性(临时变量) */
}
/* file2.c */
static void foo ( void ) /* 违规 - “ foo”不唯一(在file1.c中有全局属
* 性的同名函数) */
{
int16_t count; /* 违规 - "count" 没有全局属性, 但与另一文
* 件的有全局属性的变量重名 */
int32_t index; /* 合规 - "index"无全局属性(临时变量) */
}
- 级别:建议
- 解读:标识符名称在所有命名空间和编译单元中都应该唯一。任何标识符都不应与任何其他标识符具有相同的名称,即使该其他标识符没有链接的对象也是如此。
/* file1.c */
static int32_t count; /* "count" 局部全局属性 */
static void foo ( void ) /* "foo" 局部全局属性 */
{
int16_t count; /* 违规 - "count" 没有全局属性, 但与有局部全
* 局属性的标识符冲突 */
int16_t index; /* "index" 无全局属性 */
}
void bar1 ( void )
{
static int16_t count; /* 违规 - "count" 没有全局属性, 但与有局部全
* 局属性的标识符冲突 */
int16_t index; /* 合规 - "index" 不唯一但它没有与其冲突的具全
* 局属性的标识符 */
foo ( );
}
/* End of file1.c */
/* file2.c */
static int8_t count; /* 违规 - "count" 具有局部全局属性, 与另一个
* 具有局部全局属性的标识符重复 */
static void foo ( void ) /* 违规 - "foo" 具有局部属性, 与另一个具有局
* 部属性的函数标识符重复 */
{
int32_t index; /* 合规 - "index" 和 "nbytes" */
int16_t nbytes; /* 不唯一, 但因都不具全局属性, 因而不冲突 */
}
void bar2 ( void )
{
static uint8_t nbytes; /* 合规 - "nbytes" 不唯一, 但它没有全局属性,
* 全局属性与存储类别无关 */
}
/* End of file2.c */
- 级别:必要
- 解读:“适当的”位域类型为:
◆ C90:unsigned int 或 signed int;
◆ C99:下列几种之一:
unsigned int 或 signed int;
实现允许的其他显示声明的有符号或无符号整数类型;
_Bool
typedef unsigned int UINT_16;
struct s {
unsigned int b1:2; /* 合规 */
int b2:2; /* 违规 - 不允许使用不明确符号的"int" */
UINT_16 b3:2; /* 合规 - 由typedef声明的"unsigned int" */
signed long b4:2; /* 违规 - 即使 long 和 int 大小相同 */
};
。。。。。
MISRA 后面补充~~~~
window > Preferences > General > Workspace > Text file encoding
window > preference > General > Editors > Text Editors
选中右侧的insert space for tabs
window > preference > Editor
将workspace default
设置为Doxygen
window->preference->C++->code style->Formatter
,在右侧点击Import…
选择
xxx.xml
。
所有文件(如源文件.c,头文件.h文件,.inc文件,.def文件,编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、功能描述、修改日志等,源文件和头文件的注释中还应有模块功能简要说明。
/**
* Copyright(C), Tech. Co., Ltd.
* @brief ${project_name}
* @file ${file_name}
* @author ${user}
* @date ${date}
* -History:
* -# author: xx, date: 2022-12-27, Version:
* details:
* -# Please add new commit here.
**/
函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等。
/**
* @brief main
* @param argc numbers of parameter
* @param argv pointer array of parameter
* @return success or failed
* @retval 0 success
* @retval 1 failed
* @details program entrance
**/
int main(int argc, char* argv[])
{
//TODO
}
/* active statistic task number */
#define MAX_ACT_TASK_NUMBER 1000
#define MAX_ACT_TASK_NUMBER 1000 /* active statistic task number */
void ExampleFunc ()
{
/* This is dead loop */
DeadLoopFunc();
}
’//’
或’/*’
进行注释。注释只能用/* */
标识符。temp 可缩写为 tmp ;
flag 可缩写为 flg ;
statistic 可缩写为 stat ;
increment 可缩写为 inc ;
message 可缩写为 msg ;
unsigned int abc = 111;
unsigned int ABC = 123; /* Non-compliant */
void function()
{
unsigned int abc = 111;
unsigned int ABC = 111; /* Non-compliant */
}
uint16_t giMemorySize;
uint16_t maArray[10];
uint16_t *piPointer;
Array a...
BOOL b...
UINT n...
int i...
short n...
long l...
WORD w...
DWORD dw...
float f...
char c...
global glb
local loc
member m_
pointer prt
#define CALCULATION_PARAMETER 100
具有相似外形的字母大写和小写要区分。
是否存在下划线的标识符。
互换大写字符“O”和数字“0”
互换大写字符“I”和数字“1”
互换大写字符“I”和小写字符“l”
互换小写字符“l”和数字“1”
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
#ifndef SPI_H
#define SPI_H
…/* Contents of file */
#endif
如把“&”写成“&&”,或反之。
ret_flg = (pmsg->ret_flg & RETURN_MASK);
被写为:
ret_flg = (pmsg->ret_flg && RETURN_MASK);
rpt_flg = (VALID_TASK_NO( taskno ) && DATA_NOT_ZERO( stat_data ));
被写为:
rpt_flg = (VALID_TASK_NO( taskno ) & DATA_NOT_ZERO( stat_data ));
uint16_t GetValue()
{
return rtn_Value;
rtn_Value++; /* Non-Compliant, this is unreachable code */
}
extern uint16_t *p;
void f(f)
{
*p++; /* Non-compliant, result is not used */
(*p)++; /* Compliant, *p is incremented */
}
switch语句的Default分支作为保护作用是一个例外。
例如:
c = a + 0x16u;
应该变更为
/* .h */
#define NUMBER 0x16u
/* .c */
c = a + NUMBER;
如下表达式,考虑不周就可能出问题,也较难理解。
stat_poi ++ += 1;
++ stat_poi += 1;
应分别改为如下:
stat_poi += 1;
stat_poi++; // 此二语句功能相当于“ * stat_poi ++ += 1; ”
for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
back_sum = sum; /* backup sum */
}
语句“back_sum = sum;”完全可以放在for语句之后,如下。
for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
}
back_sum = sum; /* backup sum */
static uint16_t g_Number = 0x00;
static void f(void){
}
不可以出现如下情况:
static g_Number = 0x00;
static f();
static void function(void); /* A function declaration*/
static void function(void)
{
…/* program code */
}
extern void function1(void);
void function(void)
{
…/*program code*/
}
/* file 1 */
int16_t number = 10;
/* file 2 */
int16_t number = 20; /* Non-Compliant,two definition of number */
/* .h */
extern uint16_t g_Array[10];
/* .c */
uint16_t g_Array[10] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
enum ECU_Mode_List
{
INIT_MODE = (uint16_t)0x00u,
WORK_MODE = (uint16_t)0x01u,
DIAG_MODE = (uint16_t)0x02u,
SAFE_MODE = (uint16_t)0x03u,
SLEP_MODE = (uint16_t)0x04u,
NoofMode = (uint16_t)0x05u
};
指针被用于修改对象。
指针是被另一个指向非const指针拷贝以用于: 分配内存;内存数据转移或者被备份函数使用。
void user_copy(void *restrict p, void * restrict q, size_t n)
uint16_t Array[10] = {
0};
typedef struct
{
uint16_t a;
uint16_t b;
uint16_t c;
}structObj;
structObj Object = {
0x01, 0x02, 0x03};
uint16_t Array[] = {
0, 1, 2, 3};
不建议上述初始化方法。应该使用以下方法进行初始化。
uint16_t Array[4] = {
0, 1, 2, 3};
typedef struct
{
uint32_t m_SOF; /* Start of frame */
uint32_t m_Frame_ID; /* Frame ID */
uint32_t m_TIME_ST; /* Global time stamp */
int8_t m_AllDTCNo; /* All support DTC number */
int8_t m_DTC_Mask; /* All support status masks */
int8_t m_FramSize; /* Frame size */
int8_t m_StatusChanged; /* Status changed flag */
uint32_t m_Paddings[3]; /* Reserved */
uint32_t m_PowerOn_CNT; /* Power on counter */
}DTC_Frame_Head;
对应的结构体初始化时如下:
static DTC_Frame_Head l_SOF =
{
0xAA55AA55,
0x00000000,
0x00000000,
0x00,
0x00,
0x00,
0x00,
{
0x00000000,0x00000000,0x00000000},
0x00000000};
不可以对布尔值进行移位操作。
不可以对布尔值进行加减乘除操作。
不可以对布尔值进行求余操作。
不可以对布尔值进行位与,位或,求反操作。
不可以对有符号数进行移位操作。
不可以对8位char值进行乘除,求余,移位操作等。
不可以对float数进行移位操作。
uint16_t a = 10;
uint8_t b = 8;
b = a + b; /* Non-compliant */
应该按照下面的操作进行。
uint16_t c;
c = a + (uint16_t)b;
uint16_t u16a = 3000u;
uint8_t u8b = 50u;
u8b = u16a + u8b; /* Non-compliant */
上述赋值操作会导致数据丢失。
uint16_t u16a = 3000u;
uint8_t u8b = 50u;
uint16_t u16c = 0u;
u16c = u16a + (uint16_t)u8b; /* Compliant */
u16a = 0.1f /* Non-compliant */
u16a = -2 /* Non-compliant */
uint8_t i = 0;
for(i = 0; i <= 1000; i++)
{
…/* Non-compliant */
}
uint16_t u16a = 5000u;
uint8_t u8b = 10u;
uint32_t u32c = 0u;
u32c = (uint32_t)u16a + (uint32_t)u8b;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned long int uint32_t;
typedef signed char int8_t;
typedef short int int16_t;
typedef long int int32_t;
typedef boolean bool_t;
typedef float float32_t;
typedef double double64_t;
x = y + z * a;
应该写成
x = y + (z * a);或者 x = (y + z)*a;
u8a = u8a << 7; /* Compliant */
u8a = u8a << 8; /* Non-compliant */
u16a = (uint16_t)u8a << 10; /* Compliant */
1u << 10u /* Non-compliant */
(uint16_t)1u << 10u /* Compliant */
1UL << 10u /* Compliant */
uint16_t g_Counter = 0;
uint8_t i = 0;
for(i = 0; i < 100; i++, g_Counter++)
{
/* Non-compliant */
}
以上代码应该改为:
for(i = 0; i < 100; i++)
{
…/* program code */
g_Counter++
}
uint8_t i = 0;
for(i = 0; i <= 512; i++)
{
}
uint8_t a = 1;
if((a--) == 0)
{
/* Non-compliant */
}
应该写成:
a--;
if(a == 0)
{
…/*program code*/
}
uint8_t x = 10u;
uint8_t y = 20u;
if((x = y)>15)
{
…/* Non-compliant */
}
else
{
/* Do nothing */
}
上述应该写为以下形式:
x = y;
if(x > 15)
{
…/* program code */
}
else
{
/*Do nothing*/
}
if(ishigh || (a == f(x)))
{
/*Non-compliant*/
}
s = sizeof(j++); /* Non-compliant */
uint32_t counter = 0u;
for(float32_t f = 0.0f; f < 1.0f; f += 0.001f)
{
++counter;
}
上述编程方式是错误的,需要重写为以下形式:
float32_t f = 0.0f;
for(uint32_t counter = 0u; counter < 1000u; ++counter)
{
f = (float32_t)counter * 0.001f;
}
for(a, b, c)语句在使用中需要对a,b,c三个位置进行填写。针对填写内容进行以下规范。
a位置: 可以不进行填写。也可以填写循环计数器的初值。
b位置: 应该填写一个不具有副作用的表达式。 应该包含循环计数器和循环控制判断条件。不能包含任何在循环体内被改变内容的对象。
c位置: 包含一个仅改变循环计数器值的表达式。 不能包含任何在循环体内被改变的对象。 例外:允许使用for(;;)作为无限循环的循环体。
s8a = (u16a < 0u)? 0 : 1; /* Non-compliant, u16a always >= 0 */
int32_t i32a = 0;
if(i32a)
{
/* Non-compliant */
}
应该写成:
if(i32a != 0)
{
}
switch(state)
{
case CONDITION_1:
{
…/*program code*/
break;
}
case CONDITION_2:
{
…/*program code*/
break;
}
…………
case CONDITION_N:
{
…/*program code*/
break;
}
default:
{
…/*program code*/
break;
}
}
switch(x)
{
case CONDITION_1:
if(a == 0)
{
case CONDITION_2:
b = 0;
}
break;
}
switch(x == 0) /*Non-compliant, this is boolean value.*/
{
case 0:
break;
case 1:
break;
}
int16_t Array_1[5];
int16_t Array_2[4];
void f1(int16_t inputA[4])
{
}
void f2(int16_t inputB[])
{
}
f1(Array_1); /* Non-compliant, size of Array_1 is larger than f1 input array. */
f1(Array_2); /* Compliant */
f2(Array_1); /* Compliant */
int16_t f(void)
{
…/* program code */
}
调用时如下:
(void)f();
int16_t Array[10];
int16_t *pointer;
pointer = &Array[0];
pointer = &Array[9];
pointer = &Array[10]; /*Non-compliant, pointer is out of range.*/
、>=、<、<= 不应用在指针类型上,除非指针指向同一数组。
uint8_t *ptr;
*(ptr + 5) = 0u; /* Non-compliant */
ptr[5] = 0u; /* Compliant */
uint8_t *f(void)
{
uint8_t Local;
return &Local; /* Non-compliant */
}
#include
语句之前只能是其他预处理指令或注释。” , ”
或” \ ”
以及” /* ”
和” // ”
等非标准字符。如果在头文件名字预处理标记的 < 和 > 限定符或 ” 和 ” 限定符之间使用了 ‘ ,\ ,或 /* 字符,该行为是未定义的。
#include
只能跟随 < filename >
或 ” filename ”
序列。#undef
。#if
或#elif
预处理命令的控制表达式结果应该是0或1。#if 0, #if 1, #if A > B
#if
或#elif
预处理命令之前必须先用#define进行定义。#if
, #elif
和#endif
应该尽可能靠近且关系清晰,以防止预处理指令执行混乱。文章浏览阅读8.3k次,点赞5次,收藏12次。作为 admin 用户,请求认证令牌,输入如下命令openstack --os-auth-url http://controller:35357/v3 --os-project-domain-name default --os-user-domain-name default --os-project-name admin --os-username admin token issue报错Failed to discover available identity versions whe._caused by newconnectionerror('
文章浏览阅读4.5k次。可以在桌面安装云顷还原系统软件,利用软件中的网络对拷功能部署批量对拷环境,进行电脑教室软件的批量对拷安装与增量对拷安装。_教室电脑 一起装软件
文章浏览阅读3.1k次,点赞5次,收藏7次。原文链接:https://www.ikaze.cn/article/43写这篇博文的起因是,我在论坛宣传我开源的新项目YTask(go语言异步任务队列)时,有小伙伴在下面回了一句“为什么不用nsq?”。这使我想起,我在和同事介绍celery时同事说了一句“这不就是kafka吗?”。那么YTask和nsq,celery和kafka?他们之间到底有什么不同呢?下面我结合自己的理解。简单的分析一..._任务队列和消息队列
文章浏览阅读1.5k次。1,MyUtuils.kt将被调用的文件class MyUtils { fun show(info:String){ println(info) }}fun show(info:String){ println(info)}2,Java文件调用该类,ClientJava.javapublic class ClientJava { public static void main(String[] args) { /** _java 调用kt 对象
文章浏览阅读6.6k次,点赞4次,收藏4次。在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好? 当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,我这里仅对 像ICQ一类的发送聊天消息的情况作分析,对于其他情况,你或许也能得到一点帮助: 首先,我们知道,TCP/IP通常被认为是一个四层协议系统,包括链路层,网络层,运输层,应用层. UDP属于运输层_最大请求报文大小
文章浏览阅读10w+次,点赞14次,收藏18次。代码如下:for /l %a in (0,0,1) do echo hello,world粘贴在cmd命令行窗口中,回车即可无限死循环输出hello,world。如果需要停止,可以按ctrl+c中断。解析通用形式:for /l %variable IN (start,step,end) DO command [command-parameters] 该集表示以增量形式从start到end的一个数字序列。具体到第一段代码,如果是 (0,0,1) 就是从0开始,每次增_cmd装比代码无限循环
文章浏览阅读8.5k次,点赞2次,收藏11次。使用uni-ui UI框架实现表格加分页功能,uni-table 和uni-pagination 组件的使用示例加完整代码。_uniapp table
【代码】HTML5本地存储账号密码。
文章浏览阅读1.6k次。本小结通过transition的钩子函数实现小球半场动画头条-静敏的编程秘诀-vue教程合集知识点1:入场、出厂方法beforeEnter表示动画入场之前,此时,动画尚未开始,可以在beforeEnter中设置元素开始动画之前的起始样式enter表示动画开始之后的样式,这里可是设置小球完成动画之后的,结束状态enter(el,done)el:动画钩子函数的第一个参数:el,..._transition 钩子
主要梳理mybatis多表及动态使用
文章浏览阅读2.9w次,点赞98次,收藏777次。文章目录Qt 多线程操作2.线程类QThread3.多线程使用:方式一4.多线程使用:方式二5.Qt 线程池的使用Qt 多线程操作应用程序在某些情况下需要处理比较复杂的逻辑, 如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事件,其他线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验还可以提升程序的执行效率。Qt中使用多线程需要注意:Qt的默认线程为窗口线程(主线程):负责窗口事件处理或窗口控件数据的更新;子线程负责后台的业_qt 多线程
【代码】GQA分组注意力机制。