C/C++自包含源文件的原理与用法——以FASTLZ.C为例
这个技巧本质上是利用C预处理器的文本替换+重复展开能力,把同一个.c文件当成可复用的代码模板,通过每次包含前修改宏定义,生成多套逻辑相似但标识符不同的代码。咱们结合FASTLZ的例子拆解一下:
核心原理拆解
C预处理器处理#include指令时,只是简单地把目标文件的内容原封不动插入到当前位置——不管目标是.h还是.c。而宏定义的作用域是从定义开始到文件结束(或者被#undef取消),所以我们可以在每次包含.c文件前,定义不同的宏,让预处理器展开时生成不同的代码。
FASTLZ的具体用法分析
看你贴的代码片段,FASTLZ就是用这个技巧生成了两套压缩/解压函数:
第一套函数生成
#define FASTLZ_COMPRESSOR fastlz1_compress #define FASTLZ_DECOMPRESSOR fastlz1_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c"当预处理器展开
fastlz.c时,里面所有用到FASTLZ_COMPRESSOR的地方都会被替换成fastlz1_compress,FASTLZ_DECOMPRESSOR替换成fastlz1_decompress。相当于把fastlz.c里的核心逻辑,套上了fastlz1_*的函数名,生成第一级别的压缩/解压实现。第二套函数生成
#define FASTLZ_COMPRESSOR fastlz2_compress #define FASTLZ_DECOMPRESSOR fastlz2_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c"这次重新定义宏为
fastlz2_*,再次包含fastlz.c,预处理器会再展开一次源码,生成第二级别的压缩/解压函数——逻辑和第一套几乎一致,但函数名完全不同,对应不同的压缩级别(FASTLZ_LEVEL宏控制的细节差异)。
这么做的核心优势
- 避免代码重复:两套压缩算法的核心逻辑大同小异,只是细节(比如窗口大小、压缩率)不同,用宏替换复用同一份源码,不用复制粘贴两份几乎一样的代码,维护起来更方便。
- 符号隔离:因为用了
static修饰函数,生成的两套函数都是当前编译单元内的静态符号,不会和其他文件的同名函数冲突,也不会互相干扰。 - 性能优化:配合
FASTLZ_INLINE(本质是inline),编译器可以把这些函数内联到调用处,减少函数调用开销,提升执行效率。
关键注意点
要让这个技巧生效,fastlz.c内部必须是基于宏来编写的——比如所有函数名、甚至一些常量、逻辑分支,都依赖FASTLZ_COMPRESSOR、FASTLZ_DECOMPRESSOR或者FASTLZ_LEVEL这些宏,这样每次替换宏才能生成不同的代码。而且因为是包含.c文件,一定要用static修饰函数,否则多次包含会导致符号重复定义的编译错误。
内容的提问来源于stack exchange,提问作者Buddhika Chaturanga

