通用C语言编码规范

目录


1. 总体原则

1.1 可读性优先

  • 代码应该像散文一样易于阅读
  • 清晰的命名比短小的命名更重要
  • 一致性胜过个人偏好

1.2 简洁性

  • 避免不必要的复杂性
  • 一个函数只做一件事
  • 避免过深的嵌套

1.3 安全性

  • 检查所有输入参数
  • 避免缓冲区溢出
  • 明确处理错误情况

2. 命名约定

2.1 文件命名

1
2
3
4
5
6
7
8
9
// 源文件使用小写字母和下划线
string_utils.c
memory_pool.c
uart_driver.c

// 头文件对应
string_utils.h
memory_pool.h
uart_driver.h

2.2 函数命名

2.2.1 公共函数 - snake_case

1
2
3
4
// 公共API函数使用模块前缀
int memory_pool_init(memory_pool_t *pool, size_t size);
bool string_utils_is_empty(const char *str);
void uart_driver_send_data(const uint8_t *data, size_t len);

2.2.2 静态函数 - snake_case + 前缀

1
2
3
4
// 静态(私有)函数使用下划线前缀或模块内前缀
static bool _validate_parameters(const memory_pool_t *pool);
static void _internal_cleanup(void);
static int pool_find_free_block(memory_pool_t *pool, size_t size);

2.3 变量命名

2.3.1 局部变量 - snake_case

1
2
3
4
int buffer_size;
char *user_name;
bool is_initialized;
size_t total_count;

2.3.2 全局变量 - g_ 前缀

1
2
3
static int g_instance_count = 0;        // 模块内全局变量
extern bool g_system_ready; // 外部全局变量
static memory_pool_t g_default_pool; // 静态全局变量

2.3.3 常量 - 大写字母

1
2
3
4
5
[[define]] MAX_BUFFER_SIZE     1024
[[define]] DEFAULT_TIMEOUT_MS 5000

static const int MAX_RETRY_COUNT = 3;
static const char* const ERROR_MESSAGES[] = {...};

2.3.4 函数指针

1
2
3
4
5
6
typedef int (*error_handler_t)(int error_code);
typedef void (*callback_func_t)(void *user_data);

// 函数指针变量
error_handler_t error_handler;
callback_func_t on_complete_callback;

2.4 类型命名

2.4.1 结构体 - _t 后缀

1
2
3
4
5
6
7
8
9
10
11
typedef struct {
size_t size;
void *data;
bool is_valid;
} buffer_t;

typedef struct memory_pool {
uint8_t *pool_start;
size_t pool_size;
size_t used_size;
} memory_pool_t;

2.4.2 枚举 - _e 后缀, 成员大写

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef enum {
STATUS_SUCCESS = 0,
STATUS_ERROR_INVALID_PARAM,
STATUS_ERROR_NO_MEMORY,
STATUS_ERROR_TIMEOUT
} status_e;

typedef enum {
LOG_LEVEL_DEBUG = 0,
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR
} log_level_e;

2.4.3 联合体 - _u 后缀

1
2
3
4
5
6
7
8
9
typedef union {
uint32_t value;
struct {
uint8_t byte0;
uint8_t byte1;
uint8_t byte2;
uint8_t byte3;
} bytes;
} register_value_u;

3. 代码格式化

3.1 缩进和空格

1
2
3
4
5
6
7
8
9
10
// 使用4个空格缩进,不使用Tab
int main(void) {
if (condition) {
do_something();
if (another_condition) {
do_more_work();
}
}
return 0;
}

3.2 大括号风格 - K&R风格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 函数大括号另起一行
int function_name(int param1, char *param2)
{
// 控制结构大括号不另起一行
if (param1 > 0) {
process_data(param2);
} else {
handle_error();
}

for (int i = 0; i < param1; i++) {
work_with_index(i);
}

return result;
}

3.3 空行和空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 逻辑块之间加空行
int process_data(const char *input)
{
// 变量声明后加空行
int result = 0;
char buffer[256];

// 验证输入
if (!input) {
return -1;
}

// 处理数据
result = parse_input(input, buffer, sizeof(buffer));
if (result < 0) {
return result;
}

// 返回结果
return finalize_processing(buffer);
}

// 操作符周围加空格
int value = (a + b) * c;
bool is_equal = (x == y);
ptr->field = new_value;
array[index] = item;

3.4 行长度

  • 每行最多80个字符
  • 长行适当折断
1
2
3
4
5
6
7
8
9
10
11
12
// 函数参数过长时的折行
int very_long_function_name(int first_parameter,
const char *second_parameter,
bool third_parameter,
size_t fourth_parameter);

// 函数调用折行
result = process_complex_data(input_buffer,
buffer_size,
&output_buffer,
&output_size,
processing_flags);

4. 函数设计

4.1 函数长度

  • 函数应该简短,通常不超过50行
  • 一个函数只做一件事

4.2 参数传递

1
2
3
4
5
6
7
8
9
10
11
12
// 输入参数使用const
int string_length(const char *str);

// 输出参数使用指针
int get_system_info(system_info_t *info);

// 大结构体通过指针传递
int process_config(const config_t *config);

// 布尔返回值的函数命名
bool is_valid_address(const void *addr);
bool has_permission(int user_id, int resource_id);

4.3 返回值约定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 错误码返回值 - 0表示成功,负值表示错误
int memory_pool_alloc(memory_pool_t *pool, size_t size, void **ptr)
{
if (!pool || !ptr) {
return -1; // 参数错误
}

if (size == 0) {
return -2; // 无效大小
}

// 分配内存...

return 0; // 成功
}

// 布尔返回值
bool is_memory_available(const memory_pool_t *pool, size_t size)
{
if (!pool) {
return false;
}

return (pool->free_size >= size);
}

5. 变量声明

5.1 声明位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 在使用前就近声明
int process_array(int *array, size_t count)
{
if (!array || count == 0) {
return -1;
}

// 循环变量在循环前声明
for (size_t i = 0; i < count; i++) {
int current_value = array[i]; // 就近声明

if (current_value < 0) {
return -2;
}

array[i] = process_value(current_value);
}

return 0;
}

5.2 初始化

1
2
3
4
5
6
7
8
// 变量声明时初始化
int counter = 0;
char *buffer = NULL;
bool is_ready = false;

// 数组初始化
int values[5] = {0}; // 全部初始化为0
char name[32] = {0}; // 字符串缓冲区清零

5.3 指针声明

1
2
3
4
5
6
// *靠近变量名
char *str_ptr;
int *int_ptr;
const char *const_str;
char * const fixed_ptr;
const char * const const_str_const_ptr;

6. 结构体和枚举

6.1 结构体设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 结构体成员对齐考虑
typedef struct {
uint32_t id; // 4字节
uint16_t status; // 2字节
uint8_t flags; // 1字节
uint8_t reserved; // 1字节填充,总计8字节
void *data; // 指针大小
} packet_header_t;

// 复杂结构体
typedef struct memory_block {
struct memory_block *next; // 链表指针
size_t size; // 块大小
bool is_free; // 是否空闲
uint8_t data[]; // 灵活数组成员
} memory_block_t;

6.2 枚举设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 显式指定值
typedef enum {
UART_BAUD_9600 = 9600,
UART_BAUD_19200 = 19200,
UART_BAUD_38400 = 38400,
UART_BAUD_115200 = 115200
} uart_baud_e;

// 错误码枚举
typedef enum {
ERR_NONE = 0,
ERR_INVALID_PARAM = -1,
ERR_NO_MEMORY = -2,
ERR_TIMEOUT = -3,
ERR_NOT_INITIALIZED = -4
} error_code_e;

6.3 位域

1
2
3
4
5
6
7
// 位域结构体
typedef struct {
unsigned int enable : 1;
unsigned int mode : 2;
unsigned int priority : 4;
unsigned int reserved : 25;
} control_register_t;

7. 宏定义

7.1 简单宏

1
2
3
[[define]] MAX_PATH_LENGTH     260
[[define]] PI 3.14159265359
[[define]] NULL_CHECK(ptr) ((ptr) != NULL)

7.2 函数式宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用do-while(0)包装
[[define]] DEBUG_PRINT(fmt, ...) \
do { \
if (debug_enabled) { \
printf("[DEBUG] " fmt "\n", ##__VA_ARGS__); \
} \
} while(0)

// 安全的最大/最小值宏
[[define]] MAX(a, b) (((a) > (b)) ? (a) : (b))
[[define]] MIN(a, b) (((a) < (b)) ? (a) : (b))

// 数组大小宏
[[define]] ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

7.3 条件编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[[ifndef]] STRING_UTILS_H
[[define]] STRING_UTILS_H

[[ifdef]] __cplusplus
extern "C" {
[[endif]]

// 调试宏
[[ifdef]] DEBUG
[[define]] DBG_ASSERT(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, "Assertion failed: %s, file %s, line %d\n", \
[[expr]], __FILE__, __LINE__); \
abort(); \
} \
} while(0)
[[else]]
[[define]] DBG_ASSERT(expr) ((void)0)
[[endif]]

[[ifdef]] __cplusplus
}
[[endif]]

[[endif]] /* STRING_UTILS_H */

8. 注释规范

8.1 文件头注释

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @file memory_pool.c
* @brief 内存池管理模块实现
* @author Your Name
* @date 2024-01-01
* @version 1.0.0
*
* @details
* 提供高效的固定大小内存块管理功能,支持快速分配和释放。
* 适用于嵌入式系统中需要确定性内存管理的场景。
*
* @copyright Copyright (c) 2024 Your Company
*/

8.2 函数注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* @brief 初始化内存池
*
* @param[in,out] pool 指向内存池结构体的指针
* @param[in] buffer 内存缓冲区指针
* @param[in] size 缓冲区大小(字节)
* @param[in] block_size 单个内存块大小(字节)
*
* @return int 错误码
* @retval 0 成功
* @retval -1 参数无效
* @retval -2 缓冲区太小
*
* @note
* - buffer必须是有效的内存地址
* - size必须能容纳至少一个内存块
* - block_size必须大于0且4字节对齐
*
* @warning 调用此函数后,原缓冲区内容会被清零
*
* @example
* @code
* static uint8_t buffer[1024];
* memory_pool_t pool;
*
* int result = memory_pool_init(&pool, buffer, sizeof(buffer), 64);
* if (result != 0) {
* // 处理错误
* }
* @endcode
*/
int memory_pool_init(memory_pool_t *pool,
void *buffer,
size_t size,
size_t block_size);

8.3 行内注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int process_data(const uint8_t *data, size_t len)
{
// 参数验证
if (!data || len == 0) {
return -1;
}

uint32_t checksum = 0;

// 计算校验和
for (size_t i = 0; i < len; i++) {
checksum += data[i]; // 累加每个字节
}

checksum = ~checksum + 1; // 补码校验和

/*
* 多行注释说明复杂逻辑:
* 这里使用了特殊的校验算法,需要考虑字节序问题。
* 在小端系统上需要额外处理。
*/
[[ifdef]] LITTLE_ENDIAN
checksum = __builtin_bswap32(checksum);
[[endif]]

return (int)checksum;
}

8.4 TODO和FIXME

1
2
3
4
// TODO: 添加更多的错误检查
// FIXME: 在多线程环境下可能有竞争条件
// HACK: 临时解决方案,需要重构
// NOTE: 这个函数的行为在下个版本会改变

9. 文件组织

9.1 头文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[[ifndef]] MODULE_NAME_H
[[define]] MODULE_NAME_H

/* 1. 标准库包含 */
[[include]] <stdio.h>
[[include]] <stdlib.h>
[[include]] <string.h>

/* 2. 系统头文件 */
[[include]] <sys/types.h>
[[include]] <unistd.h>

/* 3. 项目头文件 */
[[include]] "project_config.h"
[[include]] "common_types.h"

/* 4. 本模块相关头文件 */
[[include]] "module_internal.h"

[[ifdef]] __cplusplus
extern "C" {
[[endif]]

/* 5. 宏定义 */
[[define]] MODULE_VERSION "1.0.0"
[[define]] MAX_BUFFER_SIZE 1024

/* 6. 类型定义 */
typedef struct module_context module_context_t;

/* 7. 枚举定义 */
typedef enum {
MODULE_STATE_IDLE,
MODULE_STATE_BUSY,
MODULE_STATE_ERROR
} module_state_e;

/* 8. 结构体定义 */
typedef struct {
int id;
char name[32];
module_state_e state;
} module_info_t;

/* 9. 函数声明 */
int module_init(module_context_t **ctx);
int module_process(module_context_t *ctx, const void *input);
void module_cleanup(module_context_t *ctx);

[[ifdef]] __cplusplus
}
[[endif]]

[[endif]] /* MODULE_NAME_H */

9.2 源文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* 1. 文件头注释 */
/**
* @file module_name.c
* @brief 模块实现
*/

/* 2. 包含头文件 */
[[include]] "module_name.h"
[[include]] "module_private.h"

/* 3. 本地宏定义 */
[[define]] INTERNAL_BUFFER_SIZE 256
[[define]] RETRY_COUNT 3

/* 4. 本地类型定义 */
typedef struct {
uint8_t internal_buffer[INTERNAL_BUFFER_SIZE];
size_t buffer_used;
bool is_initialized;
} module_private_t;

/* 5. 静态变量 */
static module_private_t g_module_data = {0};
static bool g_module_initialized = false;

/* 6. 静态函数声明 */
static int _validate_input(const void *input);
static void _cleanup_internal_data(void);

/* 7. 公共函数实现 */
int module_init(module_context_t **ctx)
{
// 实现...
}

/* 8. 静态函数实现 */
static int _validate_input(const void *input)
{
// 实现...
}

10. 错误处理

10.1 错误码定义

1
2
3
4
5
6
7
8
9
10
11
12
13
// 错误码枚举
typedef enum {
ERR_SUCCESS = 0, // 成功
ERR_INVALID_PARAM = -1, // 无效参数
ERR_NULL_POINTER = -2, // 空指针
ERR_NO_MEMORY = -3, // 内存不足
ERR_BUFFER_OVERFLOW = -4, // 缓冲区溢出
ERR_TIMEOUT = -5, // 超时
ERR_NOT_FOUND = -6, // 未找到
ERR_ALREADY_EXISTS = -7, // 已存在
ERR_NOT_INITIALIZED = -8, // 未初始化
ERR_OPERATION_FAILED = -9 // 操作失败
} error_code_e;

10.2 参数检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int safe_function(const char *input, char *output, size_t output_size)
{
// 空指针检查
if (!input) {
return ERR_NULL_POINTER;
}

if (!output) {
return ERR_NULL_POINTER;
}

// 参数有效性检查
if (output_size == 0) {
return ERR_INVALID_PARAM;
}

size_t input_len = strlen(input);
if (input_len >= output_size) {
return ERR_BUFFER_OVERFLOW;
}

// 执行实际操作
strcpy(output, input);

return ERR_SUCCESS;
}

10.3 错误传播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int complex_operation(const char *file_path, data_t *result)
{
int error;
file_handle_t handle;

// 打开文件
error = file_open(file_path, &handle);
if (error != ERR_SUCCESS) {
return error; // 直接传播错误
}

// 读取数据
error = file_read(handle, result);
if (error != ERR_SUCCESS) {
file_close(handle); // 清理资源
return error;
}

// 关闭文件
error = file_close(handle);
return error;
}

11. 内存管理

11.1 动态内存分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 安全的内存分配
void* safe_malloc(size_t size)
{
if (size == 0) {
return NULL;
}

void *ptr = malloc(size);
if (ptr) {
memset(ptr, 0, size); // 初始化为0
}

return ptr;
}

// 安全的内存释放
void safe_free(void **ptr)
{
if (ptr && *ptr) {
free(*ptr);
*ptr = NULL; // 防止悬空指针
}
}

// 使用示例
char *buffer = (char*)safe_malloc(256);
if (!buffer) {
return ERR_NO_MEMORY;
}

// 使用buffer...
}

safe_free((void**)&buffer); // buffer将被设置为NULL

11.2 缓冲区安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 安全的字符串复制
int safe_strcpy(char *dest, size_t dest_size, const char *src)
{
if (!dest || !src || dest_size == 0) {
return ERR_INVALID_PARAM;
}

size_t src_len = strlen(src);
if (src_len >= dest_size) {
return ERR_BUFFER_OVERFLOW;
}

memcpy(dest, src, src_len);
dest[src_len] = '\0'; // 确保以null结尾

return ERR_SUCCESS;
}

// 安全的缓冲区操作
int safe_append(char *buffer, size_t buffer_size, const char *text)
{
if (!buffer || !text || buffer_size == 0) {
return ERR_INVALID_PARAM;
}

size_t current_len = strnlen(buffer, buffer_size);
size_t text_len = strlen(text);

// 检查是否有足够空间(包括null终止符)
if (current_len + text_len + 1 > buffer_size) {
return ERR_BUFFER_OVERFLOW;
}

strcat(buffer, text);

return ERR_SUCCESS;
}

12. 最佳实践总结

12.1 代码质量检查清单

  • 所有函数都有参数检查
  • 没有硬编码的魔法数字
  • 所有分配的内存都被释放
  • 没有悬空指针
  • 缓冲区边界检查
  • 错误码正确传播
  • 函数名清晰表达意图
  • 注释解释了”为什么”而不只是”做什么”

12.2 性能考虑

  • 避免在循环中进行昂贵操作
  • 合理使用const关键字
  • 考虑内存对齐
  • 最小化函数调用开销

12.3 可维护性

  • 保持函数简短
  • 避免深层嵌套
  • 使用有意义的变量名
  • 模块化设计

12.4 可移植性

  • 避免依赖特定编译器扩展
  • 使用标准C库函数
  • 考虑字节序问题
  • 使用配置宏处理平台差异

附录:常用工具

A.1 静态分析工具

  • cppcheck - 静态代码分析
  • clang-tidy - 代码质量检查
  • splint - 代码注释检查

A.2 格式化工具

  • clang-format - 自动代码格式化
  • indent - GNU缩进工具

A.3 内存检查工具

  • valgrind - 内存泄漏检测
  • AddressSanitizer - 地址检查
  • static analysis - 静态内存分析

本编码规范遵循业界最佳实践,适用于一般性C语言项目开发。