【C/C++】C语言编程规范

C语言编码规范

参考Linux kernel代码规范,社区对于C语言编码做如下要求:

缩进

● 使用Tab作为行首缩进,而不是空格。Tab Size设为4字符

● switch语句中,case不缩进

cpp 复制代码
switch (var) {
case 0:
	
	break;
case 1:
	
	break;
default:
	
	break;
}

换行

● 每行不超过80字符

● 超过80字符时需要换行

以函数声明为例,推荐的换行方式是次行首字母与函数左括号对齐。

cpp 复制代码
static const struct uk_thread *schedprio_idle_thread(struct uk_sched *s,
													 unsigned int proc_id);

● 每行末尾不能有空白字符

● 只有换行会严重影响代码可读性时,才允许单行超过80个字符。例如banner打印等场景

空格

● C语言关键字(if、switch、case、for、do、while、return、void等)后需要添加空格

● 逗号如果不在行尾,后面需要添加空格

● 不要在圆括号周围添加空格

cpp 复制代码
if ( condition ) {
	
}


if (condition) {
	
}

● 对于指针类型定义,'*'靠近变量名、函数名

cpp 复制代码
char* name;


char *name;
char *func(int *arg);

花括号

● 对于function,将花括号("{"、"}")放置在下一行首

cpp 复制代码
void function(int arg)
{
	
}

● 除function外,将左花括号("{")放置在当前代码行,右花括号("}")另起一行。右花括号后可跟随条件语句。

cpp 复制代码
if (x == y) {
	
} else if (x > y) {
	
} else {
	
}
do {
	body of do-loop
} while (condition);

● 为降低长线维护发生逻辑错误的概率,对于条件判断等逻辑,即时是单行代码也请使用大括号

cpp 复制代码
if (x == y) {
	x++;
}

while (condition1) {
	if (condition2) {
		x++;
	} else {
		y++;
	}
}

● 为了保证代码可读性,请尽量减少花括号的嵌套层数。

cpp 复制代码
if (condition1) {
	if (condition2) {
		
	}
}


if (condition1 && condition2) {
	
}

使用typedef的场景

● 提升跨平台兼容性

● 需要隐藏具体实现

● 通过别名可以直观地识别类型信息

cpp 复制代码
typedef unsigned long int uint64_t;

typedef __u32 __lcpuid;

头文件

● 为了避免重复导入头文件,需要使用include guard宏,命名方式为__TN_{filename}H_

cpp 复制代码
#ifndef __TN_SCHEDPRIO_H__
#define __TN_SCHEDPRIO_H__

#endif

● 尽量避免C代码和汇编代码引用相同头文件。如果不得不这么做,使用__ASSEMBLY__进行区分

cpp 复制代码
#if !__ASSEMBLY__

#endif

#if __ASSEMBLY__

#endif

注释

● 采用多行注释风格

cpp 复制代码
while (condition) {
	...
}


while (condition) {
	...
}

● 使用Doxgen风格的函数注释,注释中行首缩进使用空格

cpp 复制代码
struct timer *timer_create(
	systick_t init_tick,
	void (*timeout_func)(void *parameter),
	void *parameter,
	unsigned long flag);

预处理指令注释

● 当#ifdef、#else、#endif等条件预处理指令跨越很长的代码段时,往往可读性很差。需要在分支结束处通过注释进行条件说明,注释内容为前一个分支处理的条件。

cpp 复制代码
#ifdef CONFIG_HAVE_SMP

#else 

#endif

● 对于#if、#ifdef、#elif等预处理指令,由于关键字后就是判断条件,不需要添加注释

● 预处理指令跨度较短,可以直观理解代码逻辑时,不需要添加注释

命名

● 全局变量、全局可见函数命名需要满足自描述性,不要使用不可识别的缩写

cpp 复制代码
struct thread curthr;
struct thread *curthr();


struct thread current_thread;
struct thread *current_thread();

● 采用蛇形命名,多个单词使用下划线('_')连接

cpp 复制代码
void some_function(void);
int some_var;
#define SOME_MACRO 0

宏定义

● 采用全小写字母命名函数(形式的宏),采用全大写字母命名常量

● 用于操作寄存器的函数形式的宏,可以采用全大写字母命名

● 如果函数形式的宏包含未使用的参数,请使用static inline函数,不要使用宏

● 多行语句需要使用do...while

cpp 复制代码
#define macrofun(a, b, c)		\
	do {						\
		if ((a) == 5)			\
		do_this((b), (c));		\
	} while (0)

● 不要在宏中使用return,这会导致调用该宏的函数返回

● 使用表达式定义常量时,必须用括号括起来,避免在使用时由于操作符优先级导致的异常结果;同样,对于函数形式的宏,每个参数在函数体中都要用括号括起来

cpp 复制代码
#define SOME_CONSTANT 0x80000000
#define ANOTHER_CONSTANT (0xFF | SOME_CONSTANT)

#define BASE_ADDR 0x8000
#define SOME_REGISTER(x) (BASE_ADDR + (x))

● 宏中定义临时变量时,要考虑作用域。使用常见的临时变量名,在宏展开时可能与同作用域的其他变量冲突

cpp 复制代码
#define some_function()		\
	do {
		
		int ret;

		

		
		(ret);
	} while (0)


#define some_function()		\
	do {
		
		int _some_function_ret;

		
		(_some_function_ret);
	} while (0)

函数实现

● 尽量保持函数体短小精炼,一个函数实现单一功能

● 复杂度较高的函数尽量拆分成多个子函数实现,以提升可读性和可维护性

● 如果函数中使用的临时变量超过10个,它的功能可能过于复杂了,需要考虑如何拆分

● 函数体后需要保留一行空行

cpp 复制代码
void some_function(void)
{
	
}

● 变量声明和功能代码之间需要保留一行空行

cpp 复制代码
void some_function(void)
{
	int some_var_i;
	char some_var_c;

	if (condition1) {
		some_var_i++;
	}
	
}

● 如果一个函数需要被其他微库调用,需要将函数名写入到所在微库的exportsyms.uk文件中。例如tenon/lib/tnschedprio/exportsyms.uk

函数返回值

tenon目前没有提供bool型,当函数返回一个整型值,用于表示函数功能是否异常时,遵循以下规则:

● 操作、执行、命令类函数

○ 返回 0 值代表成功,非0 值代表错误码

● 判断类函数

○ 返回 0 值代表false,1 值代表true

cpp 复制代码
int is_cpu();


int configure_gic_distriutor(void *dtb);

函数退出处理和GOTO使用

● 可以使用GOTO统一处理函数的退出逻辑(错误处理、资源回收等),但需要满足:

○ 使用GOTO可以减少条件分支,或者减少代码嵌套层数

○ 函数有多个退出点,使用GOTO将退出逻辑汇总在一处,可以减少重复代码,同时避免后续维护过程中多点修改出现遗漏

● 使用GOTO时,标签名需要有实际意义,例如说明后续代码的意图,说明走到该分支的原因等。避免使用err1、err2等无意义标签

cpp 复制代码
int testfunc(void)
{
	void *a, *b;
	int rc = 0;

	a = malloc(...);
	if (unlikely(!a)) {
		
		return -ENOMEM;
	}

	b = malloc(...);
	if (unlikely(!b)) {
		rc = -ENOMEM;
		goto out_free_a;
	}

	...

	if (condition) {
		rc = 1;
		goto out_free_b;
	}

out_free_b:
	free(b);
out_free_a:
	free(a);
	return rc;
}

● 其他场景请尽量避免使用GOTO

汇编

● 如果使用C代码能够实现相同功能,尽量不要使用汇编代码

● 指令和操作数之间使用Tab

● 存在多行汇编指令时,每条指令一行,"\n\t"结尾

● 使用asm volatile替代__asm__ volatile

cpp 复制代码
__asm__ __volatile__ (
	"instr	op, op\n\t"
	"instr2	op, op"
	:::);


asm volatile (
	"instr	op, op\n\t"
	"instr2	op, op"
	:::);

● 对于较长的内联汇编代码,尽量添加足够的注释,便于开发者理解代码功能

后续继续学习其他的规范,进行分享。

相关推荐
军训猫猫头7 分钟前
87.在线程中优雅处理TryCatch返回 C#例子 WPF例子
开发语言·ui·c#·wpf
口嗨农民工9 分钟前
mksquashfs文件系统的使用
c语言
yuanpan10 分钟前
如何将python项目打包成Windows环境的exe应用提供给客户使用
开发语言·windows·python
njsgcs21 分钟前
python getattr调用当前文件引用的模块内的方法,实例
开发语言·python
lly20240627 分钟前
R 列表:深入解析及其在数据分析中的应用
开发语言
du fei38 分钟前
C# 与 相机连接
开发语言·数码相机·c#
独好紫罗兰39 分钟前
洛谷题单3-P2669 [NOIP 2015 普及组] 金币-python-流程图重构
开发语言·python·算法
1zero1040 分钟前
[C语言笔记]09、指针
c语言·开发语言·笔记
青橘MATLAB学习44 分钟前
钢管下料问题:基于Matlab的优化求解与实践
开发语言·数学建模·matlab·钢管下料
褚翾澜1 小时前
Ruby语言的代码重构
开发语言·后端·golang