返回首页
最新
C语言没有像向量或哈希表这样的动态数据结构,因此开发者必须在数组和结构体的基础上自行开发,或者使用外部库。
其中一个最受欢迎的数据结构库是stb_ds.h。它是一个非常易于使用的单头文件库,使用起来几乎像JavaScript数组,但我对它的设计感到有些沮丧。
首先,迭代依赖于索引计算,这是一种比指针比较更慢的技术(这正是std::vector所使用的)。
其次,该结构将数组头隐藏在数据指针后面。这是为了允许使用方括号语法[ ],但这使得调试变得非常困难,因为我们无法轻松访问头部,并且这种技术依赖于未定义行为,这在不同系统上使用不太流行的编译器时尤其不可靠。
第三,stb_ds.h不进行边界检查,因此在越界访问时会静默地破坏内存。这是管理错误的最糟糕方式,但C语言在这方面的确有限。
第四,stb_ds.h并不是完全类型安全的。它依赖于sizeof(a)进行类型检查,但可以传递不同的指针类型(如char、double)、相同大小的不同基本类型(如int、float),或相同大小的不同结构体(如struct { node next; },struct { float x; float y; }),从而违反类型契约。用户必须自己强制执行类型安全。
第五,我个人不喜欢它的许可协议,因为MIT协议禁止重新许可,而公有领域在国际上是模糊的。
因此,我决定制作自己的单头文件向量库,以解决所有这些问题。
我的向量库(vector.h)使用与std::vector相同的迭代技术(3个指针,一个指向数组/元素的开始,一个指向最后一个元素之后的位置,另一个指向最后一个有效内存索引之后的位置)。这使得迭代更快(这是最常见的数组使用方式),调试更清晰,因为所有值都在结构体中,并且没有未定义行为的意外。
vector.h在出现错误时会引发异常,而不是静默地破坏内存。这看起来可能是一个糟糕的设计决定,但许多流行语言在越界访问时会崩溃(如Rust、Go、GDScript等)。这几乎总是一个严重的错误,最好通过快速失败让开发者知道。但对于那些无法承受崩溃的用户,我实现了一个标志VECTOR_NO_PANIC_ON_OOB,将越界访问转化为无操作。
类型安全的要求是最难解决的,但我最终选择了宏生成的函数。这类似于Linux内核有时使用的`list.h`,但Linux使用gnu89编译,其中包括typeof(),而我的向量库是严格遵循C89 ISO标准的,不包含typeof()。
最后,在许可方面,我选择了BSD零条款许可,这允许重新许可,在国际上没有歧义,并且不需要署名。
为了使我的库准备好投入生产,我包含了一个强健的测试例程。
我非常希望能收到那些在类似问题上挣扎的C开发者的反馈。代码和基准测试可以在[https://github.com/RolandMarchand/vector.h](https://github.com/RolandMarchand/vector.h)找到。