模块边界
C 项目变大以后,真正难维护的往往不是语法,而是模块边界。好的模块接口应该让调用方知道能做什么,不需要知道内部怎么做。
常见做法是把结构体定义隐藏在 .c 文件里,只在头文件暴露不透明类型。
struct logger;
struct logger *logger_create(const char *path);
void logger_destroy(struct logger *log);
int logger_write(struct logger *log, const char *message);
调用方不能直接访问内部字段,模块就有空间调整实现。
资源成对管理
创建和销毁函数要成对出现,命名保持一致。这样调用方一眼能看出资源生命周期。
init/deinit
create/destroy
open/close
alloc/free
不要让调用方猜一个对象应该用 free、close 还是自定义函数释放。
错误处理
C 语言没有异常,接口必须明确错误返回。常见方式是返回 0 表示成功,负数或非零表示错误。
更重要的是:失败时资源状态要可预测。函数要么完全成功,要么保持原状态,不能留下半初始化对象。
减少全局状态
全局变量会让测试、并发和复用都变困难。能通过上下文对象传递的状态,就尽量放进上下文对象。
小结
C 模块接口的目标是降低调用方心智负担。接口越稳定、语义越明确,后续维护成本越低。