目录
1、简介
”C语言是一个非常适合简洁编程的语言,但很多程序员对于简洁的关注很容易掩盖代码的真正含义”,也就是说人们都会以最快、最简洁的方式去将代码实现出来,从而忽视了代码规范,这就导致了,代码是我今天写的,但是过了一段时间我就很难理解这一段代码了。
“一个专业的程序员不仅因该能写出有效和正确的代码,他还因该能以一种可能在多年以后,需要维护它的人能够理解的风格编写的代码”。所以不管是自己独立开发的项目,还是与其他人共同开发的项目,都应该做好代码规范,这样开发和维护的过程就也会变得轻松一点。
2、代码规范
下面的代码规范主要是为自己编程制定的,这里做一个例子方便演示。更多的细节参考书籍《C Style: Standarts and Guidenlines》,这个在已最后的参考资料中给出连接。
2.1 文件布局:
如果没有对大型项目的文件进行分类,那么寻找一个文件一困难的。文件的分类布局是一个项目的开始。
第一步是划分层级(目录),以一个 STM32 的工程举例,可能这个工程包含驱动层、HAL层、App层、GUI层、lib层等,那么工程基本目录结构如图下图左边所示,文件的命名如下图右边所示,每个文件以”层级名_模块.c”的形式,如写了一个配置串口的代码,串口属于驱动层,所以将它放到 Drv 目录下,文件名为“drv_usrt.c”。
第二文件的命名和放置位置如上图所以,需要注意的是:处于低层级的函数不能调用高层级的函数,如 Hal 层里面的函数可以调用 Drv 层的函数,而 Drv 层的函数不能调用 Hal 层的函数,层级函数的调用关系如下图,Lib 层的函数可以被整个工程调用。
源文件模板(xxx.c)
/************************************************************************
* FilePath : lib_log.c
* Author : GX.Duan
* Date : 2022-09-21 22:22:33
* LastEditTime : 2022-09-21 22:28:41
* LastEditors : ShallowGreen123 2608653986@qq.com
* Copyright (c): 2022 by GX.Duan, All Rights Reserved.
* Github : https://github.com/ShallowGreen123/lvgl_mydemo
************************************************************************/
#define __LIB_LOG_C__
/*********************************************************************************
* INCLUDES
* *******************************************************************************/
#include "lib_log.h"
/*********************************************************************************
* DEFINES
* *******************************************************************************/
/*********************************************************************************
* MACROS
* *******************************************************************************/
/*********************************************************************************
* TYPEDEFS
* *******************************************************************************/
/*********************************************************************************
* STATIC FUNCTION
* *******************************************************************************/
/*********************************************************************************
* GLOBAL FUNCTION
* *******************************************************************************/
头文件模板(xxx.h)
/************************************************************************
* FilePath : drv_log.h
* Author : GX.Duan
* Date : 2022-09-21 22:22:43
* LastEditTime : 2022-09-21 22:22:59
* LastEditors : ShallowGreen123 2608653986@qq.com
* Copyright (c): 2022 by GX.Duan, All Rights Reserved.
* Github : https://github.com/ShallowGreen123/lvgl_mydemo
************************************************************************/
#ifndef _LIB_LOG_H_
#define _LIB_LOG_H_
/*********************************************************************************
* INCLUDES
* *******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/*********************************************************************************
* DEFINES
* *******************************************************************************/
#ifdef __LIB_LOG_C__
# define DEF_EXT
#else
# define DEF_EXT extern
#endif
/*********************************************************************************
* MACROS
* *******************************************************************************/
/*********************************************************************************
* TYPEDEFS
* *******************************************************************************/
/*********************************************************************************
* GLOBAL PROTOTYPES
* *******************************************************************************/
#endif /* _LIB_LOG_H_ */
2.2 命名
函数命名:层级名+模块名+功能名,如 Drv_UartSend(void); Drv_UartRecv(void); 层级名和模块名之间用“_”隔开,模块名和功能名采用“大驼峰命名法”。
void Drv_UartSend(void)
{}
void Drv_UartRecv(void)
{}
全局变量:层级名+模块名+变量名,Drv_UartSendByteCnt; Drv_UartRecvByteCnt; 层级名和模块名之间用“_”隔开,模块名和变量名采用“大驼峰命名法”。(全局变量加上 static 修饰,通过函数的形式访问。滥用全局变量的危害)
uint32_t Drv_UartSendByteCnt;
uint32_t Drv_UartRecvByteCnt;
局部变量:局部变量采用全小写,单词之间用 "_" 隔开的方式,如 uint8_t tmp; uin32_t array_idx; 函数内部的循环变量可以使用 i、j等,但不要让这样的变量命名周期过长。
void Drv_I2cTestFun(void)
{
uint8_t tmp;
uin32_t array_idx;
......
}
结构体/联合体(共用体):层级名_模块名_结构体名_t,结构体成员采用“大驼峰命名法” ,使用 typedef 新的结构体名因全部大写,联合体只需要把关键字 struct 改为 union 即可,如下所示。
// The typedef is not used
struct drv_uart_handle_t {
uint8_t Member1;
uint16_t RecvCnt;
uint32_t SendCnt;
......
};
// typedef is used
typedef struct drv_uart_handle_t {
uint8_t Member1;
uint16_t RecvCnt;
uint32_t SendCnt;
......
}DRV_UART_HANDLE_T;
枚举:层级名_模块名_枚举名_t,枚举成员采用全部大写的方式 ,使用 typedef 新的结构体名因全部大写,如下所示。
enum drv_uart_type_e {
DRV_UART_TYPE_WIFI,
DRV_UART_TYPE_BLE,
DRV_UART_TYPE_VCP,
......
};
typedef enum drv_uart_type_e {
DRV_UART_TYPE_WIFI,
DRV_UART_TYPE_BLE,
DRV_UART_TYPE_VCP,
......
}DEV_UART_TYPE_E;
宏定义:DEF_层级名_模块名_宏名;宏定义采用全大写。
#define DEF_DRV_UART_BUFF_LEN 1024
#define DEF_LIB_MATH_MAX((a),(b)) (((a) > (b))? (a) : (b))
2.3 注释
关于注释的部分,引用原文的中一段话,如下所示。“厉害的程序员不需要写注释(doge),代码的逻辑是显而易见的”。只要遵循代码规范,然后跟着实现的步骤一步步看,代码本身的逻辑也就清楚了。
如下面这个函数,首先它是 Lib 层的,属于 log 模块的,功能是根据不同 log 等级输出不同颜色的日志。以变量 str 的第一个字符作为日志等级,然后赋予输出的颜色,最后打印出来。感觉是不是不用注释,可能这个比较简单。理论上,敲代码是不需要注释的,注释留给维护的人写就好了,如果维护人员写不出来,那说明编程能力和代码规范能力还需要提升。
void Lib_LogColorPrintf(void *str, void *prefix)
{
char type = *((char *)prefix + 1);
/* clang-format off */
switch (type) {
case 'V': printf(DEF_LIB_LOG_BRIGHT_YELLOW); break;
case 'D': printf(DEF_LIB_LOG_BRIGHT_GREEN); break;
case 'I': printf(DEF_LIB_LOG_BRIGHT_WHITE); break;
case 'W': printf(DEF_LIB_LOG_BRIGHT_YELLOW); break;
case 'E': printf(DEF_LIB_LOG_BRIGHT_RED); break;
case 'X' :printf(DEF_LIB_LOG_BRIGHT_MAGENTA); break;
default:
break;
}
/* clang-format on */
printf("%s\n", (uint8_t *)str);
printf(DEF_LIB_LOG_COLOR_RESET);
}
最后,那些编程界的大佬们费尽心思设计出了各种的高级语言,其中一个原因是想提高代码的可读性,降低编程的门槛,如果没有这些高级语言,好吧......, 已经想象到程序员面对机器码、汇编时的绝望了。所以我们还是需要了解一些编程上的规范,避免将代码写成天书,避免将可读性高的语言写出没有可读性的代码,这样不就辜负了编程大佬的努力了吗。所以要好好学习代码规范,写出自己风格的代码。
3、代码格式工具的使用
这个直接在本站搜索 clang-format 的使用就可以,因该还是有比较多的解释它是怎使用的。
4、C语言常用单词缩写
单词缩写这一块建议能用缩写就用缩写,当然缩写得是那种广为人知的才可以用,不是常用的缩写还是用全拼。
参考文档 :https://download.csdn.net/download/ShallowGreen/86729786
参考资料
代码规范文档:C Style: Standarts and Guidelines
代码格式检查工具:https://clang.llvm.org/docs/ClangFormatStyleOptions.html