华为C语言编程规范重点笔记(学习C编程规范看这篇就够了) 您所在的位置:网站首页 函数说明规范写法 华为C语言编程规范重点笔记(学习C编程规范看这篇就够了)

华为C语言编程规范重点笔记(学习C编程规范看这篇就够了)

2023-12-16 21:31| 来源: 网络整理| 查看: 265

华为C编程规范原文详情:link.

一、代码总体原则 1、清晰第一 **清晰性是易于维护、易于重构的程序必需具备的特征。** ”程序必须为阅读它的人而编写,只是顺便用于机器执行“。 ”编写程序应该以人为本,计算机第二“。 一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。 2、简洁为美 **简洁就是易于理解并且易于实现。** 代码越长越难以看懂,也就越容易在修改时引入错误。 废弃的代码(没有被调用的函数和全局变量)要及时清除,重复代码应该尽可能提炼成函数。 3、选择合适的风格,与代码原有风格保持一致 **如果重构/修改其他风格的代码时,比较明智的做法是根据现有代码的现有风格继续编写代码。** 二、术语定义 **原则:**编程时必须坚持的指导思想。 **规则:**编程时强制必须遵守的约定。 **建议:**编程时必须加以考虑的约定。 **说明:**对此原则/规则/建议进行必要的解释。 **示例:**对此原则/规则/建议从正反两个方面给出例子。 **延伸阅读材料:**建议进一步参考材料。 三、具体规范介绍 1、头文件 对于C语言来说,头文件的设计体现了大部分的系统设计。不合理的头文件布局是编译时间过长的根因,不合理的头文件实际上是不合理的设计。 在一个设计良好的系统中,修改一个文件,只需要重新编译数个,甚至是一个文件。 我们应该倾向于减少包含头文件,尤其是在头文件中包含头文件,以控制改动代码后的编译时间。 原则1.1 头文件中适合放置接口的声明,不适合放置实现。 **说明:**头文件是模块(Module)或单元(Unit)的对外接口。头文件中应放置对外部的声明,如对外提供的函数声明、宏定义、类型定义等。内部使用的函数(相当于类的私有方法)声明不应放在头文件中。 内部使用的宏、枚举、结构定义不应放入头文件中。 变量定义不应放在头文件中,应放在.c文件中。变量的声明尽量不要放在头文件中,亦即尽量不要使用全局变量作为接口。 原则1.2 头文件应当职责单一 **说明:**头文件过于复杂,依赖过于复杂是导致编译时间过长的主要原因。很多现有代码中头文件过大, 职责过多,再加上循环依赖的问题,可能导致为了在.c中使用一个宏,而包含十几个头文件。 原则1.3 头文件应向稳定的方向包含 **说明:**头文件的包含关系是一种依赖,一般来说,应当让不稳定的模块依赖稳定的模块,从而当不稳定的模块发生变化时,不会影响(编译)稳定的模块。 依赖的方向应该是:产品依赖于平台,平台依赖于标准库。 除了不稳定的模块依赖于稳定的模块外,更好的方式是两个模块共同依赖于接口。 规则1.1 每一个.c文件应有一个同名.h文件,用于声明需要对外公开的接口。 **说明:**如果一个.c文件不需要对外公布任何接口,则其就不应当存在,除非它是程序的入口,如main 函数所在的文件。 规则1.2 禁止头文件循环依赖。 **说明:**头文件循环依赖,指a.h包含b.h,b.h包含c.h,c.h包含a.h之类导致任何一个头文件修改,都导致所有包含了a.h/b.h/c.h的代码全部重新编译一遍。而如果是单向依赖,如a.h包含b.h,b.h包含 c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了b.h/c.h的源代码重新编译。 规则1.3 .c/.h文件禁止包含用不到的头文件。 规则1.4 头文件应当自包含。 **说明:**简单的说,自包含就是任意一个头文件均可独立编译。如果一个文件包含某个头文件,还要包含另外一个头文件才能工作的话,就会增加交流障碍,给这个头文件的用户增添不必要的负担。 规则1.5 总是编写内部#include保护符(#define 保护)。 所有头文件都应当使用#define 防止头文件被多重包含,命名格式为FILENAME_H,为了保证唯一性,更好的命名是PROJECTNAME_PATH_FILENAME_H。 **注:**没有在宏最前面加上“_",即使用FILENAME_H代替_FILENAME_H_,是因为一般以"_"和”__"开头的标识符为系统保留或者标准库使用,在有些静态检查工具中,若全局可见的标识符以"_"开头会给出告警。 定义包含保护符时,应该遵守如下规则: 1)保护符使用唯一名称; 2)不要在受保护部分的前后放置代码或者注释。 例外情况:头文件的版权声明部分以及头文件的整体注释部分(如阐述此头文件的开发背景、使用注意事项等)可以放在保护符(#ifndef XX_H)前面。 示例:假定VOS工程的timer模块的timer.h,其目录为VOS/include/timer/timer.h,应按如下方式保护: #ifndef VOS_INCLUDE_TIMER_TIMER_H #define VOS_INCLUDE_TIMER_TIMER_H ... #endif 也可以使用如下简单方式保护: #ifndef TIMER_H #define TIMER_H .. #endif 规则1.6 禁止在头文件中定义变量。 规则1.7 只能通过包含头文件的方式使用其他.c提供的接口,禁止在.c中通过extern的方式使用外部函数接口、变量。 **说明:**若a.c使用了b.c定义的foo()函数,则应当在b.h中声明extern int foo(int input);并在a.c中通过#include 来使用foo。禁止通过在a.c中直接写extern int foo(int input);来使用foo,后面这种写法容易在foo改变时可能导致声明和定义不一致。 规则1.8 禁止在extern "C"中包含头文件。 **说明:**在extern "C"中包含头文件,会导致extern "C"嵌套,Visual Studio对extern "C"嵌套层次有限制,嵌套层次太多会编译错误。 在extern "C"中包含头文件,可能会导致被包含头文件的原有意图遭到破坏。例如,存在a.h和b.h两个头文件。 建议1.1 一个模块通常包含多个.c文件,建议放在同一个目录下,目录名即为模块名。为方便外部使 用者,建议每一个模块提供一个.h,文件名为目录名。 建议1.2 如果一个模块包含多个子模块,则建议每一个子模块提供一个对外的.h,文件名为子模块名。

**说明:**降低接口使用者的编写难度。

建议1.3 头文件不要使用非习惯用法的扩展名,如.inc。 **说明:**目前很多产品中使用了.inc作为头文件扩展名,这不符合c语言的习惯用法。 建议1.4 同一产品统一包含头文件排列方式。 **说明:**常见的包含头文件排列方式:功能块排序、文件名升序、稳定度排序。 示例1: 以升序方式排列头文件可以避免头文件被重复包含,如: #include #include #include #include #include 示例2: 以稳定度排序,建议将不稳定的头文件放在前面,如把产品的头文件放在平台的头文件前面,如下: #include #include 2、函数 函数设计的精髓:编写整洁函数,同时把代码有效组织起来。 原则2.1 一个函数仅完成一件功能。 **说明:**一个函数实现多个功能给开发、使用、维护都带来很大的困难。 原则2.2 重复代码应该尽可能提炼成函数。 **说明:**重复代码提炼成函数可以带来维护成本的降低。 规则2.1 避免函数过长,新增函数不超过50行(非空非注释行)。 **说明:**本规则仅对新增函数做要求,对已有函数修改时,建议不增加代码行。过长的函数往往意味着函数功能不单一,过于复杂。 函数的有效代码行数,即NBNC(非空非注释行)应当在[1,50]区间。 例外:某些实现算法的函数,由于算法的聚合性与功能的全面性,可能会超过50行。 规则2.2 避免函数的代码块嵌套过深,新增函数的代码块嵌套不超过4层。 说明:本规则仅对新增函数做要求,对已有的代码建议不增加嵌套层次。优秀代码参考值:[1, 4]。 规则2.3 可重入函数应避免使用共享变量;若需要使用,则应通过互斥手段(关中断、信号量)对其加以保护。 说明:可重入函数是指可能被多个任务并发调用的函数。在多任务操作系统中,函数具有可重入性是多个任务可以共用此函数的必要条件。共享变量指的全局变量和static变量。 规则2.4 对参数的合法性检查,由调用者负责还是由接口函数负责,应在项目组/模块内统一规定。 缺省由调用者负责。 规则2.5 对函数的错误返回码要全面处理。 规则2.6 设计高扇入,合理扇出(小于7)的函数。 说明:扇出是指一个函数直接调用(控制)其它函数的数目,而扇入是指有多少上级函数调用它。 规则2.7 废弃代码(没有被调用的函数和变量)要及时清除。 说明:程序中的废弃代码不仅占用额外的空间,而且还常常影响程序的功能与性能,很可能给程序的 测试、维护等造成不必要的麻烦。 建议2.1 函数不变参数使用const。 说明:不变的值更易于理解/跟踪和分析,把const作为默认选项,在编译时会对其进行检查,使代码 更牢固/更安全。 建议2.2 函数应避免使用全局变量、静态局部变量和I/O操作,不可避免的地方应集中使用。 建议2.3 检查函数所有非参数输入的有效性,如数据文件、公共变量等。 说明:函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输 入。函数在使用输入参数之前,应进行有效性检查。 建议2.4 函数的参数个数不超过5个。 说明:函数的参数过多,会使得该函数易于受外部(其他部分的代码)变化的影响,从而影响维护工作。函数的参数过多同时也会增大测试的工作量。 函数的参数个数不要超过5个,如果超过了建议拆分为不同函数。 建议2.5 除打印类函数外,不要使用可变长参函数。 说明:可变长参函数的处理过程比较复杂容易引入错误,而且性能也比较低,使用过多的可变长参函 数将导致函数的维护难度大大增加。 建议2.6 在源文件范围内声明和定义的所有函数,除非外部可见,否则应该增加static关键字。 说明:如果一个函数只是在同一文件中的其他地方调用,那么就用static声明。使用static确保只是在声明它的文件中是可见的,并且避免了和其他文件或库中的相同标识符发生混淆的可能性。 建议定义一个STATIC宏,在调试阶段,将STATIC定义为static,版本发布时,改为空,以便于后续的打热补丁等操作。 #ifdef _DEBUG #define STATIC static #else #define STATIC #endif 3、标识符命名与定义 目前比较使用的如下几种命名风格: **unix like风格:**单词用小写字母,每个单词直接用下划线„_‟分割,例如text_mutex,kernel_text_address。 **Windows风格:**大小写字母混用,单词连在一起,每个单词首字母大写。不过Windows风格如果遇到大写专有用语时会有些别扭,例如命名一个读取RFC文本的函数,命令为ReadRFCText,看起来就没有unix like的read_rfc_text清晰了。 **匈牙利命名法:**匈牙利命名主要包括三个部分:基本类型、一个或更多的前缀、一个限定词。匈牙利命名法存在较多的争议。 对标识符定 义主要是为了让团队的代码看起来尽可能统一,有利于代码的后续阅读和修改,产品可以根据自己的实际需要指定命名风格。 原则3.1 标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。 说明:尽可能给出描述性名称,不要节约空间,让别人很快理解你的代码更重要。 示例:好的命名: int error_number; int number_of_completed_connection; 不好的命名:使用模糊的缩写或随意的字符: int n; int nerr; int n_comp_conns; 原则3.2 除了常见的通用缩写以外,不使用单词缩写,不得使用汉语拼音。 规则3.1 产品/项目组内部应保持统一的命名风格。 建议3.1 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。 建议3.2 尽量避免名字中出现数字编号,除非逻辑上的确需要编号。 建议3.3 标识符前不应添加模块、项目、产品、部门的名称作为前缀。 **说明:**很多已有代码中已经习惯在文件名中增加模块名,这种写法类似匈牙利命名法,导致文件名不可读,并且带来带来如下问题:

• 第一眼看到的是模块名,而不是真正的文件功能,阻碍阅读; • 文件名太长; • 文件名和模块绑定,不利于维护和移植。若foo.c进行重构后,从a模块挪到b模块,若foo.c中有模块名,则需要将文件名从a_module_foo.c改为b_module_foo.c

建议3.4 平台/驱动等适配代码的标识符命名风格保持和平台/驱动一致。 说明:涉及到外购芯片以及配套的驱动,这部分的代码变动(包括为产品做适配的新增代码),应该保持原有的风格。 建议3.5 重构/修改部分代码时,应保持和原有代码的命名风格一致。 3.2 文件命名规则 建议3.6 文件命名统一采用小写字符。 3.3 变量命名规则 规则3.2 全局变量应增加“g_”前缀。 规则3.3 静态变量应增加“s_”前缀。 说明:增加g_前缀或者s_前缀,原因如下:首先,全局变量十分危险,通过前缀使得全局变量更加醒目,促使开发人员对这些变量的使用更加小 心。 其次,从根本上说,应当尽量不使用全局变量,增加g_和s_前缀,会使得全局变量的名字显得很丑陋,从而促使开发人员尽量少使用全局变量。 规则3.4 禁止使用单字节命名变量,但允许定义i、j、k作为局部循环变量。 建议3.7 不建议使用匈牙利命名法。 建议3.8 使用名词或者形容词+名词方式命名变量。 3.4 函数命名规则 建议3.9 函数命名应以函数要执行的动作命名,一般采用动词或者动词+名词的结构。 示例:找到当前进程的当前目录 DWORD GetCurrentDirectory( DWORD BufferLength, LPTSTR Buffer ); 建议3.10 函数指针除了前缀,其他按照函数的命名规则命名。 3.5 宏的命名规则 规则3.5 对于数值或者字符串等等常量的定义,建议采用全大写字母,单词之间加下划线„_‟的方式命 名(枚举同样建议使用此方式定义)。 规则3.6 除了头文件或编译开关等特殊标识定义,宏定义不能使用下划线„_‟开头和结尾。 4、变量 原则4.1 一个变量只有一个功能,不能把一个变量用作多种用途。 原则4.2 结构功能单一;不要设计面面俱到的数据结构。 说明:相关的一组信息才是构成一个结构体的基础,结构的定义应该可以明确的描述一个对象,而不是一组相关性不强的数据的集合。 原则4.3 不用或者少用全局变量。 规则4.1 防止局部变量与全局变量同名。 规则4.2 通讯过程中使用的结构,必须注意字节序。 规则4.3 严禁使用未经初始化的变量作为右值。 建议4.1 构造仅有一个模块或函数可以修改、创建,而其余有关模块或函数只访问的全局变量,防止多个不同模块或函数都可以修改、创建同一全局变量的现象。 说明:降低全局变量耦合度。 建议4.2 使用面向接口编程思想,通过API访问数据:如果本模块的数据需要对外部模块开放,应提供接口函数来设置、获取,同时注意全局数据的访问互斥。 建议4.3 在首次使用前初始化变量,初始化的地方离使用的地方越近越好。 建议4.4 明确全局变量的初始化顺序,避免跨模块的初始化依赖。 说明:系统启动阶段,使用全局变量前,要考虑到该全局变量在什么时候初始化,使用全局变量和初 始化全局变量,两者之间的时序关系,谁先谁后,一定要分析清楚,不然后果往往是低级而又灾难性的。 建议4.5 尽量减少没有必要的数据类型默认转换与强制转换。 5、宏、常量 规则5.1 用宏定义表达式时,要使用完备的括号。 说明:因为宏只是简单的代码替换,不会像函数一样先将参数计算后,再传递。 示例:如下定义的宏都存在一定的风险 #define RECTANGLE_AREA(a, b) a * b #define RECTANGLE_AREA(a, b) (a * b) #define RECTANGLE_AREA(a, b) (a) * (b) 正确的定义应为: #define RECTANGLE_AREA(a, b) ((a) * (b)) 规则5.2 将宏所定义的多条表达式放在大括号中。 说明:更好的方法是多条语句写成do while(0)的方式。 必须这样定义才能避免各种问题: #define FOO(x) do { \ printf("arg is %s\n", x); \ do_something_useful(x); \ } while(0) 用do-while(0)方式定义宏,完全不用担心使用者如何使用宏,也不用给使用者加什么约束。 规则5.3 使用宏时,不允许参数发生变化。 规则5.4 不允许直接使用魔鬼数字。 **说明:**使用魔鬼数字的弊端:代码难以理解;如果一个有含义的数字多处使用,一旦需要修改这个数值,代价惨重。 **解决途径:** 对于局部使用的唯一含义的魔鬼数字,可以在代码周围增加说明注释,也可以定义局部const变量,变量命名自注释。 对于广泛使用的数字,必须定义const全局变量/宏;同样变量/宏命名应是自注释的。 建议5.1 除非必要,应尽可能使用函数代替宏。 说明:宏对比函数,有一些明显的缺点: 1) 宏缺乏类型检查,不如函数调用检查严格。 2) 宏展开可能会产生意想不到的副作用,如#define SQUARE(a) (a) * (a)这样的定义,如果是SQUARE(i++),就会导致i被加两次;如果是函数调用double square(double a) {return a * a;}则不会有此副作用。 3) 以宏形式写的代码难以调试难以打断点,不利于定位问题。 4) 宏如果调用的很多,会造成代码空间的浪费,不如函数空间效率高。 建议5.2 常量建议使用const定义代替宏。 说明:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分。 建议5.3 宏定义中尽量不使用return、goto、continue、break等改变程序流程的语句。 6、质量保证 原则6.1 代码质量保证优先原则 (1)正确性,指程序要实现设计要求的功能。 (2)简洁性,指程序易于理解并且易于实现。 (3)可维护性,指程序被修改的能力,包括纠错、改进、新需求或功能规格变化的适应能力。 (4)可靠性,指程序在给定时间间隔和环境条件下,按设计要求成功运行程序的概率。 (5)代码可测试性,指软件发现故障并隔离、定位故障的能力,以及在一定的时间和成本前提下,进行测试设计、测试执行的能力。 (6)代码性能高效,指是尽可能少地占用系统资源,包括内存和执行时间。 (7)可移植性,指为了在原来设计的特定环境之外运行,对系统进行修改的能力。 (8)个人表达方式/个人方便性,指个人编程习惯。 原则6.2 要时刻注意易混淆的操作符。 原则6.3 必须了解编译系统的内存分配方式,特别是编译系统对不同类型的变量的内存分配规则,如局部变量在何处分配、静态变量在何处分配等。 原则6.4 不仅关注接口,同样要关注实现。 规则6.1 禁止内存操作越界。 规则6.2 禁止内存泄漏。 规则6.3 禁止引用已经释放的内存空间。 规则6.4 编程时,要防止差1错误。 说明:此类错误一般是由于把“”等造成的,由此引起的后果,很多情况下是很严重的,所以编程时,一定要在这些地方小心。当编完程序后,应对这些操作符进行彻底检查。使用变量时要注意其边界值的情况。 规则6.5 所有的if … else if结构应该由else子句结束 ;switch语句必须有default分支。 建议6.1 函数中分配的内存,在函数退出之前要释放。 建议6.2 if语句尽量加上else分支,对没有else分支的语句要小心对待。 建议6.3 不要滥用goto语句。 说明:goto语句会破坏程序的结构性,所以除非确实需要,最好不使用goto语句。 建议6.4 时刻注意表达式是否会上溢、下溢。 示例:如下程序将造成变量下溢。 unsigned char size ; … while (size-- >= 0) // 将出现下溢 { ... // program code } 当size等于0时,再减不会小于0,而是0xFF,故程序是一个死循环。应如下修改。 char size; // 从unsigned char 改为char … while (size-- >= 0) { ... // program code } 7、程序效率 示例:如下程序将造成变量下溢。 unsigned char size ; … while (size-- >= 0) // 将出现下溢 { ... // program code } 当size等于0时,再减不会小于0,而是0xFF,故程序是一个死循环。应如下修改。 char size; // 从unsigned char 改为char … while (size-- >= 0) { ... // program code } 原则7.1 在保证软件系统的正确性、简洁、可维护性、可靠性及可测性的前提下,提高代码效率。 本章节后面所有的规则和建议,都应在不影响前述可读性等质量属性的前提下实施。 说明:不能一味地追求代码效率,而对软件的正确、简洁、可维护性、可靠性及可测性造成影响。 原则7.2 通过对数据结构、程序算法的优化来提高效率。 建议7.1 将不变条件的计算移到循环体外。 建议7.2 对于多维大数组,避免来回跳跃式访问数组成员。 建议7.3 创建资源库,以减少分配对象的开销。 说明:例如,使用线程池机制,避免线程频繁创建、销毁的系统调用;使用内存池,对于频繁申请、释放的小块内存,一次性申请一个大块的内存,当系统申请内存时,从内存池获取小块内存,使用完毕再释放到内存池中,避免内存申请释放的频繁系统调用 建议7.4 将多次被调用的 “小函数”改为inline函数或者宏实现。 说明: 如果编译器支持inline,可以采用inline函数。否则可以采用宏。在做这种优化的时候一定要注意下面inline函数的优点:其一编译时不用展开,代码SIZE小。其二可以加断点,易于定位问题,例如对于引用计数加减的时候。其三函数编译时,编译器会做语法检查。 8、注释 原则8.1 优秀的代码可以自我解释,不通过注释即可轻易读懂。 说明:优秀的代码不写注释也可轻易读懂,注释无法把糟糕的代码变好,需要很多注释来解释的代码 往往存在坏味道,需要重构。 原则8.2 注释的内容要清楚、明了,含义准确,防止注释二义性。 原则8.3 在代码的功能、意图层次上进行注释,即注释解释代码难以直接表达的意图,而不是重复描 述代码。 说明:注释的目的是解释代码的目的、功能和采用的方法,提供代码以外的信息,帮助读者理解代码, 防止没必要的重复注释信息。 规则8.1 修改代码时,维护代码周边的所有注释,以保证注释与代码的一致性。不再有用的注释要删除。 规则8.2 文件头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者姓名、工号、内容、功能说明、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。 说明:通常头文件要对功能和用法作简单说明,源文件包含了更多的实现细节或算法讨论。 规则8.3 函数声明处注释描述函数功能、性能及用法,包括输入和输出参数、函数返回值、可重入的要求等;定义处详细描述函数功能和实现要点,如实现的简要步骤、实现的理由、设计约束等。 说明:重要的、复杂的函数,提供外部使用的接口函数应编写详细的注释。 规则8.4 全局变量要有较详细的注释,包括对其功能、取值范围以及存取时注意事项等的说明 规则8.5 注释应放在其代码上方相邻位置或右方,不可放在下面。如放于上方则需与其上面的代码用空行隔开,且与下方代码缩进相同。 规则8.6 对于switch语句下的case语句,如果因为特殊情况需要处理完一个case后进入下一个case处 理,必须在该case语句处理完、下一个case语句前加上明确的注释。 说明:这样比较清楚程序编写者的意图,有效防止无故遗漏break语句。 规则8.7 避免在注释中使用缩写,除非是业界通用或子系统内标准化的缩写。 规则8.8 同一产品或项目组统一注释风格。 建议8.1 避免在一行代码或表达式的中间插入注释。 建议8.2 注释应考虑程序易读及外观排版的因素,使用的语言若是中、英兼有的,建议多使用中文, 除非能用非常流利准确的英文表达。对于有外籍员工的,由产品确定注释语言。 建议8.3 文件头、函数头、全局常量变量、类型定义的注释格式采用工具可识别的格式。 说明:采用工具可识别的注释格式,例如doxygen格式,方便工具导出注释形成帮助文档。 9、排版与格式 规则9.1 程序块采用缩进风格编写,每级缩进为4个空格。 规则9.2 相对独立的程序块之间、变量说明之后必须加空行。 规则9.3 一条语句不能过长,如不能拆分需要分行写。一行到底多少字符换行比较合适,产品可以自行确定。 换行时有如下建议: 1)换行时要增加一级缩进,使代码可读性更好; 2)低优先级操作符处划分新行;换行时操作符应该也放下来,放在新行首; 3)换行时建议一个完整的语句放在一行,不要根据字符数断行 规则9.4 多个短语句(包括赋值语句)不允许写在同一行内,即一行只写一条语句。 规则9.5 if、for、do、while、case、switch、default等语句独占一行。 规则9.6 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后 要加空格;进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格。 说明:采用这种松散方式编写代码的目的是使代码更加清晰。 建议9.1 注释符(包括„/‟„//‟„/‟)与注释内容之间要用一个空格进行分隔。 建议9.2 源程序中关系较为紧密的代码应尽可能相邻。 10、表达式 规则10.1 表达式的值在标准所允许的任何运算次序下都应该是相同的。 将复合表达式分开写成若干个简单表达式,明确表达式的运算次序,就可以有效消除非预期副作用。 建议10.1 函数调用不要作为另一个函数的参数使用,否则对于代码的调试、阅读都不利。 说明:如下代码不合理,仅用于说明当函数作为参数时,由于参数压栈次数不是代码可以控制的,可能造成未知的输出,使用断点调试起来也比较麻烦,阅读起来也不舒服,所以不要为了节约代码行,而写这 种代码。 int g_var; int fun1() { g_var += 10; return g_var; } int fun2() { g_var += 100; return g_var; } int main(int argc, char *argv[], char *envp[]) { g_var = 1; printf("func1: %d, func2: %d\n", fun1(), fun2()); g_var = 1; printf("func2: %d, func1: %d\n", fun2(), fun1()); } 建议10.2 赋值语句不要写在if等语句中,或者作为函数的参数使用。 建议10.4 赋值操作符不能使用在产生布尔值的表达式上。 11、代码编辑、编译 规则11.1 使用编译器的最高告警级别,理解所有的告警,通过修改代码而不是降低告警级别来消除所有告警。 说明:编译器是你的朋友,如果它发出某个告警,这经常说明你的代码中存在潜在的问题。 规则11.2 在产品软件(项目组)中,要统一编译开关、静态检查选项以及相应告警清除策略。 说明:如果必须禁用某个告警,应尽可能单独局部禁用,并且编写一个清晰的注释,说明为什么屏蔽。某些语句经编译/静态检查产生告警,但如果你认为它是正确的,那么应通过某种手段去掉告警信息。 规则11.3 本地构建工具(如PC-Lint)的配置应该和持续集成的一致。 说明:两者一致,避免经过本地构建的代码在持续集成上构建失败。 规则11.4 使用版本控制(配置管理)系统,及时签入通过本地构建的代码,确保签入的代码不会影响构建成功。 说明:及时签入代码降低集成难度。 建议11.1 要小心地使用编辑器提供的块拷贝功能编程。 12、可测性 原则12.1 模块划分清晰,接口明确,耦合性小,有明确输入和输出,否则单元测试实施困难。 说明:单元测试实施依赖于: 1)模块间的接口定义清楚、完整、稳定; 2)模块功能的有明确的验收条件(包括:预置条件、输入和预期结果); 3)模块内部的关键状态和关键数据可以查询,可以修改; 4)模块原子功能的入口唯一; 5)模块原子功能的出口唯一; 6)依赖集中处理:和模块相关的全局变量尽量的少,或者采用某种封装形式。 规则12.1 在同一项目组或产品组内,要有一套统一的为集成测试与系统联调准备的调测开关及相应打印函数,并且要有详细的说明。 规则12.2 在同一项目组或产品组内,调测打印的日志要有统一的规定。 说明:统一的调测日志记录便于集成测试,具体包括: 1)统一的日志分类以及日志级别; 2)通过命令行、网管等方式可以配置和改变日志输出的内容和格式; 3)在关键分支要记录日志,日志建议不要记录在原子函数中,否则难以定位; 4)调试日志记录的内容需要包括文件名/模块名、代码行号、函数名、被调用函数名、错误码、错误发生的环境等。 规则12.3 使用断言记录内部假设。 说明:断言是对某种内部模块的假设条件进行检查,如果假设不成立,说明存在编程、设计错误。断言可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。 规则12.4 不能用断言来检查运行时错误。 说明:断言是用来处理内部编程或设计是否符合假设;不能处理对于可能会发生的且必须处理的情况要写防错程序,而不是断言。如某模块收到其它模块或链路上的消息后,要对消息的合理性进行检查,此过程为正常的错误检查,不能用断言来实现。 断言的使用是有条件的。断言只能用于程序内部逻辑的条件判断,而不能用于对外部输入数据的判断,因为在网上实际运行时,是完全有可能出现外部输入非法数据的情况。 建议12.1 为单元测试和系统故障注入测试准备好方法和通道。 13、安全性 原则13.1 对用户输入进行检查。 13.1 字符串操作安全 规则13.1 确保所有字符串是以NULL结束。 规则13.2 不要将边界不明确的字符串写到固定长度的数组中。 示例: char buff[256]; char *editor = getenv("EDITOR"); if (editor != NULL) { strcpy(buff, editor); } 上述代码读取环境变量"EDITOR"的值,如果成功则拷贝到缓冲区buff中。而从环境变量获取到的字符 串长度是不确定的,把它们拷贝到固定长度的数组中很可能导致缓冲区溢出。 正确写法:计算字符串的实际长度,使用malloc分配指定长度的内存 char *buff; char *editor = getenv("EDITOR"); if (editor != NULL) { buff = malloc(strlen(editor) + 1); if (buff != NULL) { strcpy(buff, editor); } } 13.2 整数安全 规则13.3 避免整数溢出。 说明:当一个整数被增加超过其最大值时会发生整数上溢,被减小小于其最小值时会发生整数下溢。带符号和无符号的数都有可能发生溢出。 规则13.4 避免符号错误。 说明:有时从带符号整型转换到无符号整型会发生符号错误,符号错误并不丢失数据,但数据失去了原来的含义。 规则13.5 避免截断错误。 说明:将一个较大整型转换为较小整型,并且该数的原值超出较小类型的表示范围,就会发生截断错误,原值的低位被保留而高位被丢弃。截断错误会引起数据丢失。 13.3 格式化输出安全

规则13.6 确保格式字符和参数匹配。 规则13.7 避免将用户输入作为格式化字符串的一部分或者全部。

13.4 文件I/O安全

规则13.8 避免使用strlen()计算二进制数据的长度。 规则13.9 使用int类型变量来接受字符I/O函数的返回值。 说明:字符I/O函数fgetc()、getc()和getchar()都从一个流读取一个字符,并把它以int值的形式返回。如果这个流到达了文件尾或者发生读取错误,函数返回EOF。fputc()、putc()、putchar()和ungetc()也返回一个字符或EOF。

13.5 其它 规则13.10 防止命令注入。 14 、单元测试 规则14.1 在编写代码的同时,或者编写代码前,编写单元测试用例验证软件设计/编码的正确。 建议14.1 单元测试关注单元的行为而不是实现,避免针对函数的测试。 15、可移植性 规则15.1 不能定义、重定义或取消定义标准库/平台中保留的标识符、宏和函数。 建议15.1 不使用与硬件或操作系统关系很大的语句,而使用建议的标准语句,以提高软件的可移植性和可重用性。 建议15.2 除非为了满足特殊需求,避免使用嵌入式汇编。 说明:程序中嵌入式汇编,一般都对可移植性有较大的影响。 16、业界编程规范 (1)google C++编程指南

目标: 增强代码一致性,创建通用的、必需的习惯用语和模式可以使代码更加容易理解C++是一门包含大量高级特性的巨型语言,某些情况下,我们会限制甚至禁止使用某些特性使代码简化,避免可能导致的各种问题 包含的内容: 头文件、命名规则、注释、语言特性的使用规则、编码格式 特点: 强调理解基础上的遵循,一个规则通常明确说明其优点、缺点,并举很多例子,让读者在理解的基础上遵循,不像规章制度那样生硬和抽象,实际上读起来更像一个教程。比如:禁止使用C++异常, 花了一页纸的篇幅来解释使用和不使用的优缺点,非常容易理解 推荐语: 读起来非常舒服,抛开编程规范,拿来作为理解学习C++也是不错的。

(2)汽车业C语言使用规范(MISRA)

目标: 因为编译器、编程人员理解、C语言本等原因,完全放开使用C语言存在一些风险,因此制定这个规范的目标为了促进C语言的最为安全的使用而定义一些规则。 特点: 规则都是针对的是C语言本身缺陷或容易被误解的点,如:自动变量如果不初始化就使用会出现随机值、不同类型数据赋值容易出现的隐式转换;没有包含诸如注释、变量名、编码格式等统一编程风格的内容。 推荐语: 对C的缺点了如指掌,可以帮助更好的掌握C语言,避免编程陷阱,提高程序可靠性。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有