1作者: Moowool8 个月前
C 语言本身不提供像向量(vector)或哈希映射(hashmap)这样的动态数据结构,因此开发者要么基于数组和结构体自己开发,要么使用外部库。<p>最受欢迎的数据结构库之一是 stb_ds.h。它是一个非常易于使用的单头文件库,用起来几乎就像 JavaScript 的数组,但我对它的设计感到沮丧。<p>首先,它的迭代依赖于索引计算,这比指针比较(std::vector 使用的方法)要慢。<p>其次,该结构将其数组头隐藏在数据指针之后。这样做是为了支持方括号语法 [ ],但这让调试变成了一场噩梦,因为我们无法轻松访问头部,而且这种技术依赖于未定义行为,这并不可靠,尤其是在不同系统上使用不太流行的编译器时。<p>第三,stb_ds.h 不进行边界检查,因此它会默默地破坏越界访问的内存。这是处理错误的最糟糕方式,但 C 语言在这方面有所限制。<p>第四,stb_ds.h 并非完全类型安全。它依赖于 sizeof(a) 进行类型检查,但可以传递不同的指针类型(char*,double*),相同大小的不同基本类型(int,float),或者相同大小的不同结构体(struct { node *next; },struct { float x; float y; })来违反类型契约。用户必须自己强制执行类型安全。<p>第五,我个人不喜欢它的许可方式,因为 MIT 许可证禁止再许可,而公有领域在国际上是模棱两可的。<p>所以我决定制作自己的单头文件向量库来解决所有这些问题。<p>我的向量库(vector.h)使用与 std::vector 相同的迭代技术(3 个指针,一个指向数组/元素的开头,一个指向最后一个元素之后,一个指向最后一个有效内存索引之后)。这可以实现更快的迭代(这是最常见的数组用法),更清晰的调试(所有值都在结构体中),并且不会出现未定义行为的意外情况。<p>vector.h 也会在发生错误时 panic,而不是默默地破坏内存。这看起来可能是一个糟糕的设计决策,但许多流行的语言都会在越界访问时崩溃(Rust、Go、GDScript 等)。这几乎总是一个严重的错误,最好通过快速失败来告知开发者。但对于那些无法承受崩溃的人,我实现了标志 VECTOR_NO_PANIC_ON_OOB,它将越界访问变成无操作。<p>类型安全要求是最难解决的,但我最终选择了宏生成的函数。这类似于 Linux 内核有时使用的 `list.h`,但 Linux 使用 gnu89 编译,其中包含 typeof(),而我的向量是严格的 c89 ISO 兼容,没有 typeof()。<p>最后,对于许可,我选择了 BSD Zero-Clause 许可证,该许可证允许再许可,在国际上是明确的,并且不需要署名。<p>为了让我的库可以投入生产,我包含了一个强大的测试程序。<p>我很乐意收到那些遇到过类似问题的 C 语言开发者的反馈。代码和基准测试在 <a href="https://github.com/RolandMarchand/vector.h" rel="nofollow">https://github.com/RolandMarchand/vector.h</a>