C 语言通用数据结构
更新时间:2026-08-26。本文是
languages/c/主题高手层文档。
本文要回答的问题
- C 没有模板,怎么实现"装任何类型"的容器?
void*方案和宏方案各有什么代价?- 通用性、性能、类型安全,怎么平衡?
一、三条技术路线
| 方案 | 原理 | 优点 | 代价 |
|---|---|---|---|
void* 容器 | 存指针,回调比较/打印 | 实现一次、类型无关 | 类型不安全、多一次间接 |
| 宏泛型 | #define 生成类型专用代码 | 类型安全、性能好 | 代码膨胀、难调试 |
| 内联存储 | 容器存 char buf[] 拷贝元素 | 值语义、少间接 | 需元素尺寸、复制语义 |
三条路线本质是在"灵活 / 安全 / 性能"三个角上做取舍,下面的架构图把它们各自落在哪个角标出来:

void* 把类型检查推给运行时(灵活但不安全),宏在编译期展开成专用代码(安全且快,但代码膨胀),内联存储则靠值拷贝少一次间接——选型就是看你的首要诉求是省事还是快。
二、void* 容器的设计
c
typedef struct Vec {
void **data; /* 元素数组 */
size_t len, cap;
} Vec;通用容器需要配套"元素函数":比较(cmp)、复制(clone)、释放(free)——通常以函数指针传入,与 函数指针进阶 的回调模式一脉相承。
代价:编译器不再检查类型,int 传成 double* 全靠自觉;每元素多一次指针解引用,缓存局部性也差一点。
三、什么时候选哪个
- 类型固定、性能敏感 → 宏泛型(或干脆直接写专用容器)。
- 类型多样、开发效率优先 → void 通用容器*。
- 小元素、值语义重要 → 内联存储方案。
实测:void* 容器装不同类型
c
typedef struct { void **data; size_t len; } Vec;
Vec v = {0};
int a = 10; double b = 3.14; char *s = "hello";
v.data = (void *[]){ &a, &b, s }; // 一个容器装三种类型
v.len = 3;
printf("int=%d double=%.2f str=%s\n",
*(int *)v.data[0], *(double *)v.data[1], (char *)v.data[2]);实测:
text
void*容器: int=10 double=3.14 str=hello关键代价:取用时必须手动 (int *) 强转回原类型——编译器不检查你转的对不对,int 转成 double* 也能编译通过、运行时才出问题。这就是"用类型安全换灵活性"的代价。
四、与入门层的衔接
一句话总结
C 的泛型是"用灵活换安全"的交易:void 一次实现处处可用、宏方案保住类型和性能,选型看你的首要诉求是省事还是快*。
本文已完成填充:示例代码与实测数据已补齐。