文章阐述了一些个人使用 C 编程时的习惯,主要参考一些网传的文档,固件库以及多个工程。
命名规则
结构体类型和函数的标识符一般用大驼峰式书写格式,普通变量和结构体变量的标识符则多用小驼峰式书写格式,但在此基础之上进行一定的改进不完全沿用。
宏定义的命名全部采用大写,单词之间用隔开,要求常量名只含有大写字母和下划线,且常量名用英文表达其意思不得使用拼音,当需要由多个单词表示时,单词与单词之间必须采用连字符”“连接。
全局变量、静态变量、常量尽可能的详细描述,普通局部变量和成员变量尽可能的书写简单;变量的描述都普遍按照小驼峰的方式。
这种命名法的出发点是把变量名按:属性+类型+对象描述的顺序组合起来:
由于 C 语言很多时候面向硬件开发,这边规定,硬件相关的名称全部大写,后面用“_”隔开,如:
1 2 3
| g_ :全局变量,如一个全局的长型变量定义为 g_lFailCount s_ :静态变量,如一个静态的指针变量定义为 s_plPerv_Inst c_ :常量,const 的变量 示例:const char* c_szFileName
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| bool 用b开头 b标志寄存器 int 用 i 32位开头 iCount long int 用l开头 lSum char 用c开头 cCount float 用f开头 fAvg double 用d开头 dDeta unsigned char / byte(字节) 用by开头 unsigned int(WORD) 用w开头 wCount unsigned long int(DWORD) 用dw开头 dwBroad 字符串 用s开头 sFileName 注意:以上所有类型只有在和属性部分(g_,s_,c_等)一起使用。 数组 arr:如一个 int 型数组应该表示为 arriStat。 指针 p:pData 对一重指针变量的基本原则为:“p”+变量类型前缀+命名,如一个float*型应该表示为pfStat。对二重指针变量的基本规则为:“pp”+变量类型前缀+命名。对三重指针变量的基本规则为:“ppp”+变量类型前缀+命名。 注意:虽然上面的基本类型不在局部变量中沿用,但是数组和指针类型在局部变量中也要使用,但不加入其他基本类型的符号。
|
普通局部变量,尽量做到简洁明了,毕竟函数较短,能够轻松查看原型,因此这边一般都采用直接小写的方式,尽量通过一个单词来表述意思,只有在用到指针和数组时才会加入前缀,且后面命名用大写,如:arrMember, pScore 等等。
1 2
| 最大 Max 最小 Min 初始化 Init 临时变量 T(或Temp) 源对象 Src 目的对象 Dest
|
枚举型命名类型名采用驼峰命名法,前面加EM前缀,其中的变量采用 EM_+ 大写字母类似宏定义的形式,申明为枚举型的变量使用以 em 为头的驼峰式命名(枚举变量本身就类似于常量)。
1 2 3 4 5 6 7 8
| 如: enum EMDays { EM_DAYS_MONDAY; EM_DAYS_TUESDAY; …… }; EMDays emToday;
|
结构体、类、共用体的命名规则统一采用大驼峰式命名法,结构体类型定义时在前面需要加上 Stru 前缀,结构体变量初始化申明的时候直接采用大驼峰式即可,其成员变量采用小驼峰式命名和普通变量一致。(函数与结构体变量方便区分,固都采用大驼峰式)
1 2 3 4 5 6 7
| 如: typedef struct { char *pNodeNext; char *pNodePreview; }StruStudentNode; StruStudentNode StudentNode;
|
函数的命名规则一般采用大驼峰式,命名采用“主动宾”或者“动宾”结构,与结构体变量虽然同样采用大驼峰式,但通过使用方法和命名方便进行区分。同时部分特殊函数可以采用其他命名规则,如NVIC_Configuration,I2C_init,delay_us(硬件控制,非软件延时) 硬件设备相关的使用下划线
,以此来区分哪些是特殊函数(函数与结构体方便区分采用大驼峰式)
函数参数命名规范 :
- 参数名称的命名参照变量命名规范。
- 为了提高程序的运行效率,减少参数占用的堆栈,传递大结构的参数,一律采用指针或引用方式传递。
- 为了便于其他程序员识别某个指针参数是入口参数还是出口参数,同时便于编译器检查错误,应该在入口参数前加入const标志。
1 2 3 4 5
| 如: int CmCopyString(const char * c_pcSource, char* pcDest) { ... }
|
- 普通函数与中断调用函数区别命名,普通函数如果需要设定控制终中断函数则命名以Set开头通过修改中断函数中的全局变量判定条件来控制中断执行,而中断功能函数只能被中断调用,不可直接被普通函数调用(包括主函数),命名以 Run 开头。
硬件设备名称函数命名参考 stm32 固件库命名法则:外设名称在前面全部大写
1 2 3 4 5 6 7 8 9
| 如: void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) { /* Check the parameters */ assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */ SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; }
|
除了编译开关/头文件等特殊应用,应避免使用 _EXAMPLETEST 之类以下划线开始和结尾的定义。
文件名
文件名全部使用小写字母、下划线和数字,多个具体含义的单词中用_隔开,不得出现大写字母等任何其他字符。
- Linux 系统是大小写敏感的,如果文件名只有大小写不同,则跨平台就会出问题。
- 小写文件名通常比大写文件名更易读,比如 accessibility.txt 就比 ACCESSIBILITY.TXT 易读。
- 某些系统会生成一些预置的用户目录,采用首字母大写的目录名。比如,Ubuntu 在用户主目录会默认生成Downloads、 Pictures、Documents等目录。
- 文件名全部小写,还有利于命令行操作。比如,某些命令可以不使用-i参数了。
命名具备意义
文件名(包括动态库、组件、控件、工程文件等)的命名规范,文件名的命名要求表达出文件的内容,要求文件名的长度不得少于5个字母,严禁使用象 file1,myfile 之类的文件名。
注释规范
文件注释:(使用 /*...*/
整体注释,不得采用 //
行注释)
每个文件,都必须具备文件注释,可以不完全具备下面的内容,但至少要具备:文件名、版权、简述。
详细格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| /********************* Copyright(C) 20xx company******************** File name: // 文件名 Author: // 作者、 Version: // 版本 Date: // 完成日期 Description: // 用于详细说明此程序文件完成的主要功能,与其他模块 // 或函数的接口,输出值、取值范围、含义及参数间的控 // 制、顺序、独立或依赖等关系 License: // 版权协议 Others: // 其它内容的说明 Function List: // 主要函数列表,每条记录应包括函数名及功能简要说明 1. .... History: // 修改历史记录列表,每条修改记录应包括修改日期、修改 // 者及修改内容简述 <author> <time> <version > <desc> Name1 20xx.x.x Vx.x //修改的内容 *************************************************/
|
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| /************************************ Copyright (C) 2015 HS *********************************** File name: channel.c Author: name version: V1.0 Description: 实现多个通道的脉冲发送 Others: Function List: void GetChanCtrl(void); void SetNormalMode(void); void SetImMode(void); void CtrlPulse(u8 g_byComRev); void SetTimeMode(void); u16 ScanfValue(void); u32 ScanfTime(void); void CtrlChannel(void); void InitChannel(void); void RunNormal(void); void RunPulse(void); void RunMode(void); History: <author> <time> <version > <desc> Name1 2015.6.1 V1.1 修复xx问题 **************************************************************************************************/
|
函数注释:(使用 /*...*/
整体注释,不得采用 //
行注释)
要求每个函数都需要有注释,但格式不做强制要求,功能复杂的函数必须具备:函数描述、参数说明、返回值;较为简短的函数可以简述功能。
以下为一个比较详细的格式说明:
1 2 3 4 5 6 7 8 9 10 11 12 13
| /************************************************************************** Function: // 函数名称 Description: // 函数功能、性能等的描述 Calls: // 被本函数调用的函数清单 Called By: // 调用本函数的函数清单 Table Accessed: // 被访问的表(此项仅对于牵扯到数据库操作的程序) Table Updated: // 被修改的表(此项仅对于牵扯到数据库操作的程序) Input: // 输入参数说明,包括每个参数的作 // 用、取值说明及参数间关系。 Output: // 对输出参数的说明。 Return: // 函数返回值的说明 Others: // 其它说明 **************************************************************************/
|
参考案例:
1 2 3 4 5 6 7 8 9 10
| /************************************************************************ Function: InitChannel Description: 启动初始化所有通道 Calls: TIM_DeInit TIM3_Int_Init Called By: main() Input: 无 Output: 让所有通道输出低电平 Return: 无 ************************************************************************/
|
程序块注释:
一般的程序块注释如 for,while 之类的注释在前面,程序排版整齐,并方便注释的阅读与理解,将注释与其上面的代码用空一行隔开,且该程序块与下面的代码也应该用空一行(使用/\…*/整体注释,不得采用//行注释)*
1 2 3 4 5 6 7 8 9 10 11 12
| 例: void example_fun( void ) { /* code one comments */ CodeBlock One /* code two comments */ CodeBlock Two /* code three comments */ CodeBlock Three }
|
边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。命名一般要采用自注释的命名规则(详见命名规范),对于单行代码注释如:函数调用、变量说明等,则直接在行后使用 //
注释。
参考链接:
http://itec.hust.edu.cn/~xujing/cpp/common-cpp/text-book/C-coding-rules-simple.pdf
https://github.com/ruanyf/document-style-guide