C 编码规范

        

目录

一、命名

1、通用规范

2、文件名

3、变量名

4、函数名

5、宏名

二、变量

三、函数

1、重复代码尽可能提炼为函数

2、函数不超过 50 行

3、代码块嵌套不超过四层

4、可重入函数避免使用共享变量

5、对参数的合法性进行检查

6、设计合理的函数错误码

7、函数输入输出通过参数传递

8、设计高扇入合理扇出的函数

9、函数不变的参数使用 const 修饰

10、应检查非参数输入的有效性

11、函数的参数不应超过五个

12、除打印函数外,不使用可变长参函数

13、不对外暴露的函数应使用 static 修饰

四、宏

1、表达式善用完备的小括号

2、多条表达式用 do-while(0) 方式定义

3、不对宏参数进行操作

4、尽可能使用函数代替宏

5、常量可以使用 const 替代宏

6、格外注意魔鬼数字

7、宏内不使用改变流程的语句

五、头文件

1、除入口主函数,每个 C 文件都应该有一个头文件

2、禁止头文件的循环依赖

3、禁止包含用不到的头文件

4、头文件应该自包含

5、应当编写头文件保护符

6、禁止在头文件中定义变量

六、质量保证

1、了解编译系统的内存分配方式

2、注意接口实现的隐含约束

3、禁止内存操作越界

4、禁止内存泄漏

5、禁止引用已经释放了的空间

6、编程时,要防止差 1 的错误

8、不要使用 goto 语句。

9、注意表达式是否会上溢、下溢

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

11、避免跳跃式访问数组成员

12、创建资源库以减少分配对象的开销

13、频繁调用的 “ 小函数” 改为 inline 函数或者宏实现

七、注释

1、优秀的代码可以自注释

2、注释内容要清楚、明了

3、对功能和意识层次进行注释

4、持续维护代码的注释

5、文件注释

6、函数注释

7、全局变量注释

附录

1、常见缩写

2、常见反义词


        编码习惯因人而异,这边博客主要是自己编码习惯的一个总结,同时结合网络上的一些优秀编码规范进行完善。

        同时这篇博客的内容不会太长,不会对规范进行详细介绍,主要起到一个快速索引的作用,以便需要的时候立即查看。

        非常推荐阅读网络上流传的《华为C语言编程规范》。

一、命名

1、通用规范

(1)统一采用 unix 命名法,字母小写与下划线结合使用。

(2)命名需含义明确,自注释。不使用含义不明的缩写。常见缩写,见附录。

(3)使用正确的反义词表示互斥。常见缩写,见附录。

2、文件名

(1)统一采用小写字符;

(2)C 文件和对应的头文件 H 文件同名

(3)文件名可以是项目缩写_模块名称,例如 ngx_file、ngx_inet 等

3、变量名

(1)全局变量应增加“ g_” 前缀;静态变量应增加“ s_” 前缀;

(2)禁止使用单字节命名变量,但可将 i、j、k 作为局部循环变量;

(3)不使用匈牙利命名法;

(4)使用名词或者形容词+名词方式命名变量;

4、函数名

(1)函数命名应以函数要执行的动作命名,一般采用动词或者动词+名词的结构,例 print_record();

(2)函数指针除了前缀,其他按照函数的命名规则命名;

5、宏名

(1)对于数值和字符串等常量的定义,全部大写,单词之间通过 "_" 连接;

(2)除了头文件或编译开关等特殊标识定义,不能使用下划线 "_" 开头和结尾;

(3)命名同变量名:使用名词或者形容词+名词方式命名变量

(4)头文件保护符:PROJECTNAME_PATH_FILE_NAME_H

二、变量

(1)不用或少用全局变量作为共享变量;

(2)定义结构体结构时需要注意内存对齐,否则可能占用过多内存;

(3)网络通讯过程中需要注意字节序的变化:发送前应该进行主机序到网络序的转换;接收时也必须进行网络序到主机序的转换;

(4)面向接口编程思想,通过 API 访问数据;如果本模块的数据需要对外部模块开放,应提供接口函数来设置、获取,同时注意全局数据的访问互斥;

(5)变量定义时最好先初始化,或者初始化与定义的近些;

// 较好的初始化:使用默认有意义的初始化
int speedup_factor = -1;
if (condition)
{speedup_factor = 2;
}//较好的初始化:使用 ?: 减少数据流和控制流的混合
int speedup_factor = condition? 2: -1;//较好的初始化:使用函数代替复杂的计算流
int speedup_factor = ComputeSpeedupFactor();

(6)全局变量的初始化和首次使用需要考虑时序;

(7)尽量减少数据类型默认转换与强制转换;

(8)建议不要在参数中做对变量进行操作(自增或自减),因为可能引用的接口函数,在某个版本升级后,变成了一个兼容老版本所做的一个宏,结果可能不可预知。

三、函数

1、重复代码尽可能提炼为函数

        应当使用代码重复度检查工具,在持续集成环境中持续检查代码重复度指标变化趋势,并对新增重复代码及时重构。

        当一段代码重复两次时,即应考虑消除重复,当代码重复超过三次时,应当立刻着手消除重复

2、函数不超过 50 行

3、代码块嵌套不超过四层

        函数的代码块嵌套深度指的是函数中的代码控制块(例如:if、for、while、switch等)之间互相包含的深度。

        嵌套太多,难以阅读

4、可重入函数避免使用共享变量

        必须使用共享变量时,需要加锁互斥

5、对参数的合法性进行检查

        对参数的合法性检查,由调用者或函数实现负责;

        建议由函数实现负责,因为调用者水平未知;

6、设计合理的函数错误码

        函数可以返回错误码,标识函数的执行状态;

        定义时,可通过枚举,或者宏定义进行定义;

7、函数输入输出通过参数传递

8、设计高扇入合理扇出的函数

        扇出是指一个函数直接调用(控制)其它函数的数目,而扇入是指有多少上级函数调用它(最好小于7)

9、函数不变的参数使用 const 修饰

        不变的值更易于理解/跟踪和分析,把const作为默认选项,在编译时会对其进行检查,使代码更牢固/更安全。

10、应检查非参数输入的有效性

        函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。函数在使用输入参数之前,应进行有效性检查

11、函数的参数不应超过五个

12、除打印函数外,不使用可变长参函数

13、不对外暴露的函数应使用 static 修饰

四、宏

        需要明确一点:宏只是简单的代码替换。

1、表达式善用完备的小括号

        用宏定义表达式时,使用完备的小括号

// 如下定义的宏都存在一定的风险,例如 c/RECTANGLE_AREA(a, b),会变为 c/a * b #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))

2、多条表达式用 do-while(0) 方式定义

        将宏所定义的多条表达式放在大括号中,并且用 do-while(0) 方式定义宏,完全不用担心使用者如何使用宏,也不用给使用者加什么约束;

#define FOO(x) do { \printf("arg is %s\n", x); \do_something_useful(x); \
} while(0)

3、不对宏参数进行操作

        使用宏时,不允许参数发生变化,例如自增与自减

4、尽可能使用函数代替宏

        除非必要,应尽可能使用函数代替宏

        主要是以下考虑:

        (1)宏缺乏类型检查,不如函数调用检查严格;

        (2)以宏形式写的代码难以调试难以打断点,不利于定位问题;

        (3)宏如果调用的很多,会造成代码空间的浪费,不如函数空间效率高;

5、常量可以使用 const 替代宏

6、格外注意魔鬼数字

        对于代码中的魔鬼数字需要尽可能多的注释。对于局部使用的可以定义局部 const 变量,对于全局广泛使用的必须定义const 全局变量或宏。

        魔鬼数字既是含义不明的数字;

7、宏内不使用改变流程的语句

        宏定义中尽量不使用 return 、 goto 、 continue 、 break 等改变程序流程的语句

五、头文件

1、除入口主函数,每个 C 文件都应该有一个头文件

2、禁止头文件的循环依赖

3、禁止包含用不到的头文件

4、头文件应该自包含

其他头文件包含这个头文件,即可使用该头文件的所有接口,不需要再额外包含其他头文件

5、应当编写头文件保护符

命名规范:PROJECTNAME_PATH_FILE_NAME_H

6、禁止在头文件中定义变量

        包含该头文件的所有变量都将重复定义该变量

六、质量保证

1、了解编译系统的内存分配方式

        了解编译系统的内存分配方式,特别是编译系统对不同类型的变量的内存分配规则,如局部变量在何处分配、静态变量在何处分配等。

2、注意接口实现的隐含约束

        接口的实现除了和调用者传递的参数相关,往往还受制于其他隐含约束,如:物理内存的限制,网络状况,具体看“抽象漏洞原则”。

3、禁止内存操作越界

        内存操作主要是指对数组、指针、内存地址等的操作。内存操作越界是软件系统主要错误之一,后果非常严重。

        有以下措施防止内存越界:

        (1)数组的大小要考虑最大情况,避免数组分配空间不够;

        (2)避免使用危险函数 sprintf /vsprintf/strcpy/strcat/gets 操作字符串,使用相对安全的函数 snprintf/strncpy/strncat/fgets代替;

        (3)使用 memcpy/memset 时一定要确保长度不要越界

        (4)字符串考虑最后的 ’\0’, 确保所有字符串是以 ’\0’ 结束

        (5)指针加减操作时,考虑指针类型长度

        (6)数组下标进行检查

        (7)使用时 sizeof 或者 strlen 计算结构/字符串长度,避免手工计算

4、禁止内存泄漏

        内存和资源(包括定时器/文件句柄/Socket/队列/信号量/GUI等各种资源)泄漏是常见的错误,例如异常出口处没有释放内存。

        有以下措施防止内存泄漏:

        (1)异常出口处检查内存、定时器/文件句柄/Socket/队列/信号量/GUI等资源是否全部释放

        (2)删除结构指针时,必须从底层向上层顺序删除

        (3)使用指针数组时,确保在释放数组时,数组中的每个元素指针是否已经提前被释放了

        (4)避免重复分配内存

        (5)小心使用有return、break语句的宏,确保前面资源已经释放

        (6)检查队列中每个成员是否释放

5、禁止引用已经释放了的空间

        在实际编程过程中,稍不留心就会出现在一个模块中释放了某个内存块,而另一模块在随后的某个时刻又使用了它。要防止这种情况发生。

        有以下措施防止引用已经释放了的空间:

        (1)内存释放后,把指针置为NULL;使用内存指针前进行非空判断。

        (2)耦合度较强的模块互相调用时,一定要仔细考虑其调用关系,防止已经删除的对象被再次使用。

        (3)避免操作已发送消息的内存。

        (4)自动存储对象的地址不应赋值给其他的在第一个对象已经停止存在后仍然保持的对象(具有更大作用域的对象或者静态对象或者从一个函数返回的对象)

6、编程时,要防止差 1 的错误

        要防止把 “=” 误写成 “>” 等错误。编完程序后,应对这些操作符进行彻底检查。使用变量时要注意其边界值的情况。

7、所有的 if ... else if 结构应该由 else 子句结束,对没有 else 分支的 if 语句要小心对待 ; switch 语句必须有 default 分支。

8、不要使用 goto 语句

9、注意表达式是否会上溢、下溢

/*unsigned char 变量大于 0,不会出现小于 0 的情况。当 size 等于 0 时,再减不会小于 0,而是 0xFF,故程序是一个死循环
*/
unsigned char size ;
…
while (size-- >= 0) // 将出现下溢
{
... // program code
}/*应如下修改
*/
char size; // 从unsigned char 改为char
…
while (size-- >= 0)
{
... // program code
}

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

11、避免跳跃式访问数组成员

        多维数组在内存中是从最后一维开始逐维展开连续存储的。所以对于多维大数组,避免来回跳跃式访问数组成员

12、创建资源库以减少分配对象的开销

        例如,使用线程池机制,避免线程频繁创建、销毁的系统调用;使用内存池,对于频繁申请、释放的小块内存,一次性申请一个大块的内存,当系统申请内存时,从内存池获取小块内存,使用完毕再释放到内存池中,避免内存申请释放的频繁系统调用

13、频繁调用的 “ 小函数” 改为 inline 函数或者宏实现

        如果编译器支持 inline,可以采用 inline 函数。否则可以采用宏。

        在做这种优化的时候一定要注意下面 inline 函数的优点:

        其一编译时不用展开,代码SIZE小;

        其二可以加断点,易于定位问题,例如对于引用计数加减的时候;

        其三函数编译时,编译器会做语法检查。三思而后行

七、注释

1、优秀的代码可以自注释

2、注释内容要清楚、明了

        清楚、明了、含义准确,防止注释二义性;

3、对功能和意识层次进行注释

       对代码的功能和意识层次进行注释,不要重复描述代码的功能;

4、持续维护代码的注释

        修改代码时,需要维护代码的注释,保证代码与注释的一致性;

5、文件注释

        可参考如下规范

/*****************************************************************************
Copyright: 1988-1999, Huawei Tech. Co., Ltd.
File name: 文件名
Description: 用于详细说明此程序文件完成的主要功能,与其他模块或函数的接口,输出
值、取值范围、含义及参数间的控制、顺序、独立或依赖等关系
Author: 作者
Version: 版本
Date: 完成日期
History: 修改历史记录列表,每条修改记录应包括修改日期、修改
者及修改内容简述。
*****************************************************************************/

6、函数注释

        函数声明处注释描述函数功能、性能及用法,包括输入和输出参数、函数返回值、可重入的要求等;定义处详细描述函数功能和实现要点,如实现的简要步骤、实现的理由、设计约束等;

/*************************************************
Function: // 函数名称
Description: // 函数功能、性能等的描述
Calls: // 被本函数调用的函数清单Input: // 输入参数说明,包括每个参数的作
用、取值说明及参数间关系。
Output: // 对输出参数的说明。
Return: // 函数返回值的说明
Others: // 其它说明
*************************************************/

7、全局变量注释

        全局变量要有较详细的注释,包括对其功能、取值范围以及存取时注意事项等的说明。

/* The ErrorCode when SCCP translate */
/* Global Title failure, as follows */ /* 变量作用、含义*/
/* 0 -SUCCESS 1 -GT Table error */
/* 2 -GT error Others -no use */ /* 变量取值范围*/
/* only function SCCPTranslate() in */
/* this modual can modify it, and other */
/* module can visit it through call */
/* the function GetGTTransErrorCode() */ /* 使用方法*/
BYTE g_GTTranErrorCode;

 

附录

1、常见缩写

缩写

解释

缩写

解释

arg

argument(参数)

cfg

configuration(配置)

buff

buffer

dev

device(设备)

clk

clock(时钟)

err

error

cmd

command(命令)

hex

hexadecimal(十六进制)

cmp

compare(比较)

inc

increment(增)

init

initialize(初始化)

prev

previous(上一个)

max

reg

register(注册)

min

sem

semaphore(信号)

msg

message(消息)

stat

statistic(统计)

sync

synchronize(同步)

tmp

temp(临时)

src

source

dst

destination

recv

receive(接收)

2、常见反义词

add/remove

insert/delete

increment/decrement

lock/unlock

old/new

begin/end

source/destination

first/last

put/get

open/close

start/stop

show/hide

copy/paste

create/destroy

get/release

add/delete

min/max

next/previous

send/receive

up/down

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/692999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue前端docx库生成word表格 并合并单元格的例子

Vue.js 是一个流行的前端JavaScript框架,用于构建用户界面和单页应用程序。在Vue中生成Word表格并合并单元格,通常需要使用额外的库,如docx,它是一个用于创建和修改Word文档(.docx)的JavaScript库。 …

JAVA并发编程之原子性、可见性与有序性

并发编程-原子性、可见性与有序性 一、CPU的可见性 1.1 缓存一致性问题的出现 CPU处理器在处理速度上,远胜于内存,主内存执行一次内存的读写操作,所需要的时间足够处理器去处理上百条指令。 为了弥补处理器与主内存处理能力之间的差距&am…

将phantomjs制成docker镜像

几个前的一篇文章中介绍了phantomjsecharts生成图表图片的一种方式,但其部署复杂,制作为docker镜像运行就方便多了。文章参见:https://blog.csdn.net/u011943534/article/details/121524397 1、准备echarts 将上次文章中提到过下载的Echart…

(三)Spring 核心之面向切面编程(AOP)—— 代理的创建

目录 一. 前言 二. 代理的创建 2.1. 创建前准备 2.2. 获取所有的 Advisor 2.3. 创建代理的入口方法 2.4. 依据条件创建代理(JDK 或 CGLIB) 三. 动态代理要解决什么问题 3.1. 什么是代理 3.2. 什么是动态代理 四. 总结 一. 前言 前面两篇文章《…

MyBatis学习总结

MyBatis分页如何实现 分页分为 逻辑分页:查询出所有的数据缓存到内存里面,在从内存中筛选出需要的数据进行分页 物理分页:直接用数据库语法进行分页limit mybatis提供四种方法分页: 直接在sql语句中分页,传递分页参数…

网贷大数据查询多了对征信有影响吗?

网贷大数据在日常的金融借贷中起到很重要的风控作用,不少银行已经将大数据检测作为重要的风控环节。很多人在申贷之前都会提前了解自己的大数据信用情况,那网贷大数据查询多了对征信有影响吗?本文带你一起去看看。 首先要说结论:那就是查询网…

[极客大挑战2019]upload

该题考点&#xff1a;后缀黑名单文件内容过滤php木马的几种书写方法 phtml可以解析php代码&#xff1b;<script language"php">eval($_POST[cmd]);</script> 犯蠢的点儿&#xff1a;利用html、php空格和php.不解析<script language"php"&…

寄存器 Flip-Flop

组合逻辑是电平输入和电平输出。&#xff08;组合逻辑虽然符合人的思维习惯&#xff0c;并且元器件结构简单&#xff0c;但问题是如果输入含有毛刺&#xff0c;输出就有毛刺。eg. 如果输入信号突然从0变成1后又在短时间内恢复0&#xff0c;可以视为毛刺&#xff0c;输出信号受到…

说说 Dubbo 工作原理?Dubbo 容错策略?Zookeeper 和 Dubbo 的关系?Dubbo 动态代理策略有哪些?Dubbo 负载均衡策略?

说说 Dubbo 工作原理? 工作原理分 10 层&#xff1a; 第一层&#xff1a; service 层&#xff0c;接口层&#xff0c;给服务提供者和消费者来实现的&#xff08;留给开发人员来实现&#xff09;&#xff1b; 第二层&#xff1a; config 层&#xff0c;配置层&#xff0c…

Oracle的TimesStamp和Date的区别

在Oracle数据库中&#xff0c;DATE和TIMESTAMP数据类型都是用于存储日期和时间信息&#xff0c;但它们之间有几个重要的区别&#xff1a; 精度不同&#xff1a; DATE数据类型能存储日期和时间到秒的精度&#xff0c;格式通常是YYYY-MM-DD HH24:MI:SS&#xff0c;并且它总是包含…

软件测试工程师经典面试题

软件测试工程师&#xff0c;和开发工程师相比起来&#xff0c;虽然前期可能不会太深&#xff0c;但是涉及的面还是比较广的。前期面试实习生或者一年左右的岗位&#xff0c;问的也主要是一些基础性的问题比较多。涉及的知识主要有MySQL数据库的使用、Linux操作系统的使用、软件…

缓存驱动联邦学习架构赋能个性化边缘智能 | TMC 2024

缓存驱动联邦学习架构赋能个性化边缘智能 | TMC 2024 伴随着移动设备的普及与终端数据的爆炸式增长&#xff0c;边缘智能&#xff08;Edge Intelligence, EI&#xff09;逐渐成为研究领域的前沿。在这一浪潮中&#xff0c;联邦学习&#xff08;Federated Learning, FL&#xf…

golang 如何防止内存逃逸

在Go语言中&#xff0c;内存逃逸是指在函数中分配的变量在函数结束后仍然被引用&#xff0c;从而导致变量的生命周期延长&#xff0c;被分配在堆上而不是栈上。防止内存逃逸有助于提高程序的性能&#xff0c;因为栈上分配的内存可以更快地被回收。 以下是一些防止内存逃逸的方…

leetcode hot100零钱兑换Ⅱ

本题可以看出也是背包问题&#xff0c;但区别于之前的01背包问题&#xff0c;这个是完全背包问题的变形形式。 下面介绍01背包和完全背包的区别与联系&#xff1a; 01背包是背包中的物品只能用一次&#xff0c;不可以重复使用&#xff0c;而完全背包则是可以重复使用。01/完全…

为BUG编程:char的默认类型导致的BUG

char的默认类型大部分是signed&#xff0c;教科书上也这么讲&#xff0c;所以一般遇不到什么问题&#xff0c;但是在arm上出问题了&#xff0c;默认char是unsigned&#xff0c;导致c<0永远失败。 代码是这样的&#xff1a; //计算输出长度size_t outputlen(string const &am…

一个基于C#开发的、开源的特殊字符输入法

emoji表情在社交网络非常流行&#xff0c;我们在手机也非常方便输入&#xff0c;但是在PC电脑我们一般需要到归集好的网页拷贝&#xff0c;所以今天推荐一个Windows小工具&#xff0c;让你方便输入特殊字符和emoji表情。 01 项目简介 这是一个基于C#开发的开源项目&#xff0…

JCL中的位置参数

JCL中DD语句的位置参数 ​ DD语句的格式&#xff1a; ​ //DD名 DD 位置参数 ​ DD名&#xff0c;是DD语句定义的名字&#xff0c;由1-8个字符组成&#xff0c;一个STEP里面可以有多个DD语句&#xff0c;每个DD语句指向一个系统中的数据资源&#xff0c;这就是为了操纵数据文…

ansible及其模块

一、ansible是什么&#xff1f; Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。 Ansible能批量配置、部…

手动实现new操作符

<script>//前置知识// 每一个函数在创建之初就会有一个prototype属性&#xff0c;这个属性指向函数的原型对象// function abc(){// }// abc.prototype--> {constructor: f}// 在JS中任意的对象都有内置的属性叫做[[prototype]]这是一个私有属性&#xff0c;这个私有属…

如何用GPT进行论文写作?

一&#xff1a;AI领域最新技术 1.OpenAI新模型-GPT-5 2.谷歌新模型-Gemini Ultra 3.Meta新模型-LLama3 4.科大讯飞-星火认知 5.百度-文心一言 6.MoonshotAI-Kimi 7.智谱AI-GLM-4 二&#xff1a;GPT最新技术 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#x…