当前位置: 首页 > article >正文

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

C语言编码规范

参考Linux kernel代码规范,社区对于C语言编码做如下要求:
缩进
● 使用Tab作为行首缩进,而不是空格。Tab Size设为4字符
● switch语句中,case不缩进

switch (var) {
case 0:
	
	break;
case 1:
	
	break;
default:
	
	break;
}

换行
● 每行不超过80字符
● 超过80字符时需要换行
以函数声明为例,推荐的换行方式是次行首字母与函数左括号对齐。

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等)后需要添加空格
● 逗号如果不在行尾,后面需要添加空格
● 不要在圆括号周围添加空格

if ( condition ) {
	
}


if (condition) {
	
}

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

char* name;


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

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

void function(int arg)
{
	
}

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

if (x == y) {
	
} else if (x > y) {
	
} else {
	
}
do {
	body of do-loop
} while (condition);

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

if (x == y) {
	x++;
}

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

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

if (condition1) {
	if (condition2) {
		
	}
}


if (condition1 && condition2) {
	
}

使用typedef的场景
● 提升跨平台兼容性
● 需要隐藏具体实现
● 通过别名可以直观地识别类型信息

typedef unsigned long int uint64_t;

typedef __u32 __lcpuid;

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

#ifndef __TN_SCHEDPRIO_H__
#define __TN_SCHEDPRIO_H__

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

#if !__ASSEMBLY__

#endif

#if __ASSEMBLY__

#endif

注释
● 采用多行注释风格

while (condition) {
	...
}


while (condition) {
	...
}

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

struct timer *timer_create(
	systick_t init_tick,
	void (*timeout_func)(void *parameter),
	void *parameter,
	unsigned long flag);

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

#ifdef CONFIG_HAVE_SMP

#else 

#endif

● 对于#if、#ifdef、#elif等预处理指令,由于关键字后就是判断条件,不需要添加注释
● 预处理指令跨度较短,可以直观理解代码逻辑时,不需要添加注释
命名
● 全局变量、全局可见函数命名需要满足自描述性,不要使用不可识别的缩写

struct thread curthr;
struct thread *curthr();


struct thread current_thread;
struct thread *current_thread();

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

void some_function(void);
int some_var;
#define SOME_MACRO 0

宏定义
● 采用全小写字母命名函数(形式的宏),采用全大写字母命名常量
● 用于操作寄存器的函数形式的宏,可以采用全大写字母命名
● 如果函数形式的宏包含未使用的参数,请使用static inline函数,不要使用宏
● 多行语句需要使用do…while

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

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

#define SOME_CONSTANT 0x80000000
#define ANOTHER_CONSTANT (0xFF | SOME_CONSTANT)

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

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

#define some_function()		\
	do {
		
		int ret;

		

		
		(ret);
	} while (0)


#define some_function()		\
	do {
		
		int _some_function_ret;

		
		(_some_function_ret);
	} while (0)

函数实现
● 尽量保持函数体短小精炼,一个函数实现单一功能
● 复杂度较高的函数尽量拆分成多个子函数实现,以提升可读性和可维护性
● 如果函数中使用的临时变量超过10个,它的功能可能过于复杂了,需要考虑如何拆分
● 函数体后需要保留一行空行

void some_function(void)
{
	
}

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

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

int is_cpu();


int configure_gic_distriutor(void *dtb);

函数退出处理和GOTO使用
● 可以使用GOTO统一处理函数的退出逻辑(错误处理、资源回收等),但需要满足:
○ 使用GOTO可以减少条件分支,或者减少代码嵌套层数
○ 函数有多个退出点,使用GOTO将退出逻辑汇总在一处,可以减少重复代码,同时避免后续维护过程中多点修改出现遗漏
● 使用GOTO时,标签名需要有实际意义,例如说明后续代码的意图,说明走到该分支的原因等。避免使用err1、err2等无意义标签

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

__asm__ __volatile__ (
	"instr	op, op\n\t"
	"instr2	op, op"
	:::);


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

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

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


http://www.kler.cn/a/456343.html

相关文章:

  • 六大基础深度神经网络之CNN
  • 连锁餐饮行业数据可视化分析方案
  • 【Rust自学】7.4. use关键字 Pt.1:use的使用与as关键字
  • JavaScript中new关键字调用一个函数
  • python流行的web框架对比分析
  • 链式二叉树的基本操作,前序、中序以及后序遍历(递归实现,非递归实现)【有图解】
  • pthread_create概念和使用案例
  • DeepSeek-V2:强大、经济且高效的专家混合语言模型
  • AIDD - 人工智能药物设计 -使用 Butina 模块对相似化合物进行聚类
  • vue2前端导出pdf文件
  • stm32基础(keil创建、Proteus仿真、点亮LED灯,7段数码管)
  • AI + 爬虫:智能化数据采集的未来
  • 转义特殊token is all you need
  • 已有docker镜像构建过程分析
  • Redis:高性能内存数据库的深度探索
  • 第三百四十六节 JavaFX教程 - JavaFX绑定
  • 解释为什么fetch(JavaScript)无法将读取的数据存入外部变量
  • JVM简介—JVM的执行子系统
  • 企业架构学习笔记-数字化转型
  • 华为管理变革之道:奋斗文化与活力
  • 软路由系统 iStoreOS 中部署 Minecraft 服务器
  • Redis+注解实现限流机制(IP、自定义等)
  • SqlSugar配置连接达梦数据库集群
  • C#WPF基础介绍/第一个WPF程序
  • 【RabbitMQ的死信队列】
  • CCF-GESP 等级考试 2023年12月认证C++二级真题解析