C语言编程规范笔记(华为)

华为 (特殊规范已经省略,如:全局变量应增加“g_”前缀。)

一、头文件

原则

  1. 头文件只放置声明
  2. 头文件应当职责单一
  3. 头文件应当向稳定方向包含,不稳定模块依赖于稳定模块。

规则

  1. 每一个.c文件都应有一个同名.h文件,用于对外公开接口

  2. 禁止头文件循环依赖(现在循环依赖编译不会通过)

  3. .c/.h文件禁止包含用不到的头文件
    减少编译时间

  4. 头文件应当自包含,头文件可独立编译

  5. 编写#include保护符(#define保护)

#ifndef __STM32F10x_IT_H
#define __STM32F10x_IT_H
		...
#endif /* __STM32F10x_IT_H */
  1. 禁止在头文件中定义变量(原则1)
  2. 禁止在.c中通过extern的方式使用外部函数接口、变量
    不要直接在a.c中extern int foo(int input);来使用foo。
    应当在b.h中声明extern int foo(int input);并在a.c中通过#include <b.h>来使用foo
  3. 禁止在extern "C"中包含头文件。

建议

  1. 建议每一个模块提供一个.h,文件名为目录名。
  2. 建议每一个子模块提供一个对外的.h,文件名为子模块名。
  3. 头文件不要使用非习惯用法的扩展名,如.inc。
  4. 同一产品统一包含头文件排列方式。

由于操作失误,导致2-3章原始笔记丢失,使用简单笔记作为补充
原因:文章已经发布之后,不会自动保存正在编辑的内容,过程中断网,导致原始笔记丢失。

二、函数

背景:函数设计的精髓:编写整洁函数,同时把代码有效组织起来。

原则

  1. 一个函数仅完成一件功能。
  2. 重复代码应该尽可能提炼成函数。
  3. 避免函数过长,新增函数不超过50行(非空非注释行)。
  4. 避免函数的代码块嵌套过深,新增函数的代码块嵌套不超过4层。

规则

  1. 避免函数过长,新增函数不应超过50行。
  2. 避免函数的代码块嵌套过深,新增函数的代码块嵌套不超过4层。
  3. 可重入函数应避免使用共享变量;若需要使用,则应通过互斥手段(关中断、信号量)对其加以保护。
  4. 参数的合法性检查,一般由调用者负责。
  5. 对函数的错误返回码要全面处理。
  6. 设计高扇入,合理扇出(小于7)的函数。
  7. 废弃代码(没有被调用的函数和变量)要及时清除。

建议

  1. 函数不变参数使用const。
  2. 函数应避免使用全局变量、静态局部变量和I/O操作,不可避免的地方应集中使用。
  3. 检查函数所有非参数输入的有效性,如数据文件、公共变量等。
  4. 函数的参数个数不超过5个。
  5. 除打印类函数外,不要使用可变长参函数
  6. 在源文件范围内声明和定义的所有函数,除非外部可见,否则应该增加static关键字。
    只对内的函数增加static关键字

三、标识符命名与定义

1. 通用命名规则

原则

  1. 标识符的命名要清晰、明了,有明确含义。
    缩写少,可读性高

  2. 除了常见的通用缩写以外,不使用单词缩写,不得使用汉语拼音 。
    汉语拼音

    一些常见可以缩写的例子:
    argument 可缩写为 arg
    buffer 可缩写为 buff
    clock 可缩写为 clk
    command 可缩写为 cmd
    compare 可缩写为 cmp
    configuration 可缩写为 cfg
    device 可缩写为 dev
    error 可缩写为 err
    hexadecimal 可缩写为 hex
    increment 可缩写为 inc、
    initialize 可缩写为 init
    maximum 可缩写为 max
    message 可缩写为 msg
    minimum 可缩写为 min
    parameter 可缩写为 para
    previous 可缩写为 prev
    register 可缩写为 reg
    semaphore 可缩写为 sem
    statistic 可缩写为 stat
    synchronize 可缩写为 sync
    temp 可缩写为 tmp

规则

  1. 产品/项目组内部应保持统一的命名风格。

建议

  1. 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。

    add/remove begin/end create/destroy
    insert/delete first/last get/release
    increment/decrement put/get add/delete
    lock/unlock open/close min/max
    old/new start/stop next/previous
    source/target show/hide send/receive
    source/destination copy/paste up/down

  2. 尽量避免名字中出现数字编号,除非逻辑上的确需要编号。

  3. 标识符前不应添加模块、项目、产品、部门的名称作为前缀。

  4. 平台/驱动等适配代码的标识符命名风格保持和平台/驱动一致。

  5. 重构/修改部分代码时,应保持和原有代码的命名风格一致。

2. 文件命名规则

建议

  1. 文件命名统一采用小写字符。

3. 变量命名规则

规则

  1. 禁止使用单字节命名变量,但允许定义i、j、k作为局部循环变量。

建议

  1. 不建议使用匈牙利命名法。 (目前仍在使用)
  2. 使用名词或者形容词+名词方式命名变量

4. 函数命名规则

建议

  1. 函数命名应以函数要执行的动作命名,一般采用动词或者动词+名词的结构。

5. 宏的命名规则

规则

  1. 对于数值或者字符串等等常量的定义,建议采用全大写字母,单词之间加下划线„_‟的方式命
    名(枚举同样建议使用此方式定义)。

    #define PI_ROUNDED 3.14

  2. 除了头文件或编译开关等特殊标识定义,宏定义不能使用下划线„_‟开头和结尾。

四、变量

原则

  1. 一个变量只有一个功能,不能把一个变量用作多种用途。
  2. 设计功能单一的数据结构,不要面面俱到
  3. 不用少用全局变量。

规则

  1. 防止局部变量与全局变量同名。
    同名会导致使用混淆。
  2. 通讯过程中使用的结构,必须注意字节序。
    暂时用不上这一条。
  3. 严禁使用未经初始化的变量作为右值。
    没有初始化,不能作为右值。

建议

  1. 构造仅有一个模块或函数可以修改、创建,而其余有关模块或函数只访问的全局变量,防止多个不同模块或函数都可以修改、创建同一全局变量的现象。

  2. 使用面向接口编程思想,通过API访问数据,提供接口函数设置,获取

  3. 在首次使用前初始化变量,初始化的地方离使用的地方越近越好。

    不要使用无意义的初始化:speedup_factor = 0;

  4. 明确全局变量的初始化顺序,避免跨模块的初始化依赖。 要考虑到该全局变量在什么时候初始化,使用全局变量和初始化全局变量,两者之间的时序关系,谁先谁后。

  5. 尽量减少没有必要的数据类型默认转换与强制转换。

五、宏 常量

规则

  1. 用宏定义表达式时,要使用完备的括号。
    宏只是简单的代码替换,不会像函数一样计算参数先后

  2. 将宏所定义的多条表达式放在大括号中。
    暂时用不到,看不太懂

  3. 使用宏时,不允许参数发生变化。

    示例:如下用法可能导致错误。
    #define SQUARE(a) ((a) * (a))
    int a = 5;
    int b;
    b = SQUARE(a++); // 结果:a = 7,即执行了两次增。
    个人觉得上述代码应该写成函数(例子罢了,正常人不会这样写)

  4. 不允许直接使用魔鬼数字。
    魔鬼数字的定义:在代码中没有具体含义的数字、字符串。
    解决:用常量定义魔鬼数字

建议

  • 除非必要,应尽可能使用函数代替宏。
    正如上述代码所说

  • 常量建议用const代替宏
    出错跟踪的时候可见源头而不是一个数字,但是很多嵌入式开发中,宏定义利大于弊。

  • 程序流程语句不要在宏定义中使用,如:return,goto,break,continue。

六、质量保证

原则

  1. 代码质量保证优先。
    正确,简洁,可维护,可靠,可测试,性能,可移植,个人表达

  2. 注意容易混淆的操作符。
    易混淆如:= 和 == ;!(逻辑操作)和~(位操作);
    易用错如:

    /操作,两边是整形,结果也是整形,向下取整。
    %取余操作只能用整数
    自加自减操作符的前置和后置

  3. 了解内存分配方式,不同编译系统变量分配规则。

  4. 不仅关注接口,也要关注实现。

规则

  1. 禁止内存操作越界。
    - 数组考虑最大情况,避免空间不足。
    - 避免使用危险函数sprintf /vsprintf/strcpy/strcat/gets操作字符串,使用相对安全的函数snprintf/strncpy/strncat/fgets代替。
    - 使用memcpy/memset时一定要确保长度不要越界。
    - 考虑字符串结尾是’\0’,确保字符串是以’\0’结束。
    - 指针加减考虑指针类型长度。
    - 数组下标检查。
    - 使用sizeof/strlen检查长度,避免手工检查。

  2. 禁止内存泄漏。

  3. 禁止引用已经释放的内存空间。

  4. 编程时,要防止差1错误。
    比如"<=“和”<"的使用

  5. 有if最后要有else,有switch要有default。

建议

  1. 函数分配内存,要在退出之前释放。

  2. 规则5

  3. 不要goto

  4. 注意表达式的上溢下溢

     unsigned char size;
     ...
     while(size-- >= 0) //将出现下溢
     {
    	...
     }	
     //当size等于0,继续减不会小于0,而是0xFF,死循环
    

七、程序效率

原则

  1. 在保证软件系统的正确性、简洁、可维护性、可靠性及可测性的前提下,提高代码效率。
    将大概率出现的分支放在前面,虽然可以提高效率,但是大多数时候,不要把注意力集中在如何使代码更快上,应首先关注让代码尽可能地清晰易读和更可靠。

  2. 通过对数据结构、程序算法的优化来提高效率。

建议

  1. 将不是每次循环都要做的操作,放在循环体外,避免无效操作。
  2. 对于多维大数组,避免来回跳跃式访问数组成员。 (暂时没有遇到这个问题)
  3. 创建资源库,接触较少。
  4. 将多次调用的小函数改为内联函数或者宏来实现

八、注释

原则

  1. 好的代码可以自我注释,不通过注释可以读懂。
    好的函数名,变量名,宏定义……
  2. 注释不要有二义性。可以自己读几遍检查。
  3. 注释是解释代码的功能,意图。
    1. 不要复述代码,不要无用信息。
    2. 对于实现代码中巧妙的、晦涩的、有趣的、重要的地方加以注释,出彩的或复杂的代码块前要加注释

规则

  1. 修改原来的注释,保证注释和代码的一致性。
  2. 文件头部应进行注释(有模板)
  3. 函数声明处注释描述函数功能、性能及用法。(入参要说明清楚)
  4. 全局变量要有全面的注释:功能取值范围以及存储注意事项。
  5. 注释位置:上方相邻或者右方,不要在下方。
  6. switch里case语句结束时如果进入下一个case,要注释去了哪里,避免无故遗漏break。
  7. 注释避免缩写。
  8. 统一注释风格。

建议

  1. 代码和表达式中间不要插入注释。
  2. 尽量使用中文。
  3. 文件头,函数头,全局变量常量,类型定义使用注释格式采用工具可识别的格式。

九、排版与格式

规则

  1. 缩进风格编写。
  2. 相对独立的程序块之间一行隔开。
  3. 一条语句不要过长,过长要分行写。
  4. 一行一条语句。
  5. if、for、do、while、case、switch、default等语句独占一行。
  6. 对等操作加空格,不对等操作不加空格。

建议

  1. 注释符和内容加空格。
  2. 关系紧密的代码尽量相邻。

十、表达式

规则

  1. 运算次序的问题不能使用括号来解决。
    自增自减运算符:和表达式,函数使用分开
  2. 不要嵌套赋值。

建议

  1. 函数调用不要作为另一个函数的参数使用,否则对于代码的调试、阅读都不利。
  2. 赋值语句不要写在if中或者作为函数的参数。
  3. 一条语句使用不同操作符需要使用到括号。
  4. 赋值操作符不能使用在产生布尔值的表达式上。

简单来说,赋值操作最好分开写。

十一、代码编译

注意告警清除,修改代码清除告警

十二、可测性

原则

  1. 模块划分清晰,接口明确,耦合小,明确输入输出。
  2. 编写为集成测试的调测开关和打印函数。
  3. 使用断言记录内部假设,不能用断言来检查运行时错误。(暂时不会使用)

十三、安全性

原则

用户输入检查。(函数入参检查)

  • 用户输入作为数值的,做数值范围检查
  • 用户输入作为数值的,做数值范围检查
  • 用户输入是字符串的,检查字符串长度
  • 用户输入作为格式化字符串的,检查关键字“%”
  • 用户输入作为业务数据,对关键字进行检查、转义

规则

  1. 字符串以NULL即’\0’结尾,避免使用一些危险函数。

    //正确写法:截断字符串,保证字符串以NULL结束。
    char a[16];
    strncpy(a, "0123456789abcdef", sizeof(a) - 1 );
    a[sizeof(a) - 1] = '\0';
    
  2. 明确字符串长度再写入数组,分配内存

  3. 避免整型变量溢出

  4. 避免带符号整型和无符号整型的符号错误。

  5. 避免截断错误,大整型到小的转换

  6. 格式化输出参数匹配 %d,int,懂?

  7. 不要将用户输入直接输出

    printf(input);//可能会出错
    printf(“%s”, input);//正确
    
  8. 避免使用strlen()计算二进制数据的长度。

十四、单元测试

规则

  1. 编写单元测试用例验证软件设计/编码的正确。

建议

  1. 单元测试关注行为,不关注其中函数的测试。

十五、可移植性

规则

  1. 不要改标准库

建议

  1. 除非必要情况避免嵌入式汇编(尽量不要去汇编代码)。
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、清晰第一 清晰性是易于维护、易于重构的程序必需具备的特征。代码首先是给人读的,好的代码应当可以像文章一样发声朗诵出来。 目前软件维护期成本占整个生命周期成本的40%~90%。根据业界经验,维护期变更代码的成本,小型系统是开发期的5倍,大型系统(100万行代码以上)可以达到100倍。业界的调查指出,开发组平均大约一半的人力用于弥补过去的错误,而不是添加新的功能来帮助公司提高竞争力。 一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。 2、简洁为美 简洁就是易于理解并且易于实现。代码越长越难以看懂,也就越容易在修改时引入错误。写的代码越多,意味着出错的地方越多,也就意味着代码的可靠性越低。因此,我们提倡大家通过编写简洁明了的代码来提升代码可靠性。 废弃的代码(没有被调用的函数和全局变量)要及时清除,重复代码应该尽可能提炼成函数。 3、选择合适的风格,与代码原有风格保持一致 产品所有人共同分享同一种风格所带来的好处,远远超出为了统一而付出的代价。在公司已有编码规范的指导下,审慎地编排代码以使代码尽可能清晰,是一项非常重要的技能。 如果重构/ / 修改其他风格的代码时,比较明智的做法是根据 现有 代码 的 现有风格继续编写代码,或者使用格式转换工具进行转换成公司内部风格。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值