C语言编程规范(个人整理)
前言
写这篇博客主要是希望自己能够按照这篇编程规范进行编程。
1、基本命名规则
规则:
- 文件名,函数,结构体,联合体,枚举 --> 大驼峰
- 变量,函数参数,宏参数,结构体字段,联合体成员 --> 小驼峰
- 宏,常量,枚举值,goto标签 --> 全大写,下划线分割
- 全局变量应增加 'g_'前缀,局部静态变量不加前缀
- 指针变量使用’p’前缀
- bool类型变量使用’b’前缀
建议:
- 作用域越大,命名应越精确,包括函数和变量
- 动作类函数,可以使用动宾结构,如:AddUser(),DeleteUser()
- 判断型函数,可以使用形容词或加is,如:DataReady(),IsWorking()
- 数据型函数,如:GetTotalCount()
- 局部变量命名应该简短,且能表达相关含义
- 通过 typedef 对结构体,联合体,枚举起别名时,尽量使用匿名类型,如:
typedef struct{
char userName;
int userId;
}User;
- 需要指针自嵌套,可以增加’tag’前缀,如:
typedef struct tagTaskList{
int userData;
struct tagTaskList *pPrev;
struct tagTaskList *pNext;
}TaskList;
2、排版规则
规则:
- 循环语句,条件语句必须要大括号,即便是空语句或只有一条语句
- 禁止 if / else / else if 写同一行
- if 左括号和 if 同一行,右括号和 else 以及 else 左括号一行,for 循环同理
- 多个变量定义和赋值语句不能写在同一行
- 代码缩进要求是4个空格
- 函数中的不同代码块之间要空一行,注释写在代码块前,如:
int GetTotalCount(int *arr, int len){
int count = 0;
//notes:
for(int i = 0; i < len; i++){
if(arr[i] > =5){
count++;
}
}
return count;
}
3、编程规则
规则:
- 每一个 .c 文件都需要对应 .h 文件,用于放置对外提供的函数声明、宏定义、类型定义等,不对外使用的函数可以用 static 限制
- 包含头文件时,应先包含稳定的头文件,顺序:标准C函数库,第三方库,自己写的头文件
#include <stdio.h> //标准c库
#include <string.h> //标准c库
#include <srt/srt.h> //srt协议第三方库
#include "Test.h" //自己写的
- 使用 #ifndef 防止头文件重复包含(或者#pragma once),如Test.h
#ifndef TEST_H_
#define TEST_H_
...
...
#endif /* TEST_H_ */
- 尽量不用 extern 调用外部变量和函数,应将这些变量和函数放在 .h 文件中,以供外部使用
- 调用函数要判断返回值,尤其这个返回值将被后续函数调用时,调用前要先检查返回值是否合理
- 如果不关心返回值,应将函数声明为void类型
- 函数指针的指针参数,应确定是否使用const修饰
- 多个 .c 文件要调用同一个内联函数,那么这个内联函数应该在 .h 中定义(否则无法内联,因为内联函数是在编译阶段展开的), 且内联函数不超过10行
- 宏函数并没有类型检查,可以考虑用内联函数代替
- 全局变量要少用,其它 .c 文件需要用到其他文件的全局变量时,尽量用函数接口提供全局变量
- #ifdef __cplusplus 可以用来避免C编译器编译 extern C 而出错
#ifdef __cplusplus
extern "C"{
#endif
...
...
#ifdef __cplusplus
}
#endif
4、使用宏定义打印Debug信息
- 使用断言函数,函数原型 void assert( int expression ),实际上是一个宏定义函数。 如果表达式为真,继续执行;如果表达式为假,打印错误信息,调用 abort() 函数终止程序运行。此外,在 assert.h 头文件前使用#define NDEBUG 可以关闭断言函数。
#include <stdio.h>
//#define NDEBUG //可以关闭assert断言函数
#include <assert.h> //断言函数头文件
int main(){
int ret = GetCount();
assert(ret >= 0); //用于判断函数返回值,ret < 0 程序终止
return;
}
- 使用自定义的宏函数,如下:
#define PRINT_LOG 1
#define PRINT_ALOG 0
#if PRINT_LOG
#if PRINT_ALOG
#define ALOG(fmt, ...) printf("%s:%s:%d "fmt, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);
#define LOG(fmt, ...) ALOG(fmt, __VA_ARGS__) //advance print 携带文件名、函数名、行号信息
#else
#define OLOG(fmt, ...) printf(fmt, __VA_ARGS__)
#define LOG(fmt, ...) OLOG(fmt, __VA_ARGS__) //ordinary print
#endif
#else
#define LOG(fmt, ...)
#endif
//用于打印字符串 如:LOGSTR("Init successed\n");
#define LOG_STR 1
#if LOG_STR
#define LOGSTR(...) printf(__VA_ARGS__)
#else
#define LOGSTR(...)
#endif
/*
* 使用方式:
* LOG用于打印带格式输出的字符串。
* 在宏定义PRINT_ALOG为0时,打印ind和count信息;
* 在宏定义PRINT_ALOG为1时,能够额外打印文件名、函数名、行号信息。
* LOG("ind:%d\t count:%d\n", ind, count);
* LOG_STR用于打印不带格式输出的字符串
* LOG_STR("Init successed\n");
*/