文章摘要
Martin Uecker探讨了在C语言中实现类型和边界安全的通用容器,特别是受Haskell启发的maybe类型,用于处理可能不存在的返回值,如除零错误。通过宏定义,maybe类型可以扩展为结构体,并定义简单的类型构造函数,调用者可以检查值是否存在。
文章总结
C语言中的泛型容器:使用Maybe实现安全除法
Martin Uecker在2025年8月10日发表了一篇文章,讨论了如何在C语言中实现类型和边界安全的泛型容器。此前,他探讨了span类型、使用数组进行边界检查以及向量类型。
这次,他介绍了受Haskell启发的maybe类型。该类型用于返回一个可能不存在的值,例如在计算过程中遇到错误时。以下是一个捕获除零错误的divide函数示例:
c
static maybe(int) divide(int a, int b)
{
return (b != 0) ? maybe_just(int, a / b) : maybe_nothing(int);
}
但需要注意的是,这里还有一个未检查的错误情况。具体是什么?
maybe类型可以通过宏定义为结构体,并定义简单的类型构造函数:
```c
define maybe(T) struct maybe_##T { bool ok; T value; }
define maybe_just(T, x) (maybe(T)){ .value = (x), .ok = true }
define maybe_nothing(T) (maybe(T)){ .value = (T){ }, .ok = false }
```
在调用者中,可以检查值是否存在:
```c int main() { int d = 2; // 0
maybe(int) p = divide(6, d);
if (p.ok) {
printf("%d\n", p.value);
} else {
printf("division by zero\n");
fflush(stdout);
}
return 0;
} ```
为了更安全地使用,可以添加一个包含检查的宏maybe_value:
```c
define maybevalue(T, x) (*({ maybe(T) *p = &(x); p->ok ? &p->value : (void*)0; }))
```
在错误情况下,该宏会创建一个指向空值的左值,依赖空指针检查器将其转换为运行时陷阱以确保安全。
此外,文章还提到整数除法在C语言中可能存在的另一个未定义行为:当最小的可表示整数除以-1时,结果会超出最大可表示整数范围。因此,文章提供了一个更安全的unsafe_divide函数,并最终修正为safe_divide函数:
```c maybe(int) safedivide(int a, int b) { if (b == 0 || (b == -1 && a == INTMIN)) return maybe_nothing(int);
return maybe_just(int, a / b);
} ```
通过使用GCC的符号溢出检查器,文章展示了优化器如何静态证明函数中没有溢出或除零错误。然而,这种方法并不能证明C程序的完全内存安全,因为有些领域(如生命周期问题和指针算术)并未被检查器覆盖。
最后,文章提到了作者正在实验的库,并邀请读者提供改进建议。
评论总结
GCC生成的汇编代码与C++的std::optional
相似 - 主要观点:GCC为C++的std::optional
生成的汇编代码基本相同,但结果不能通过寄存器返回,除非函数被内联。 - 关键引用:
- "GCC generates essentially the same assembly for C++'s std::optional
with the exception that the result can't be returned in a register." - "GCC为C++的std::optional
生成的汇编代码基本相同,但结果不能通过寄存器返回。"
- "GCC generates essentially the same assembly for C++'s std::optional
- 主要观点:GCC为C++的std::optional
未定义行为的质疑
- 主要观点:评论者质疑代码中依赖空指针消毒剂来触发运行时陷阱的做法是否属于未定义行为。
- 关键引用:
- "Isn't this undefined behavior?"
- "这是未定义行为吗?"
对复杂性的批评
- 主要观点:评论者认为这种实现引入了过多的隐藏复杂性,建议作者考虑使用其他语言,或者这只是作者的一个趣味性挑战。
- 关键引用:
- "You are introducing insane amounts of hidden complexity."
- "你引入了过多的隐藏复杂性。"
对API设计的建议
- 主要观点:评论者建议使用更符合函数式编程风格的API设计,如maybedivide -> maybe(int) -> maybe(int) -> maybe(int),并增加andthen()、or_else()等操作。
- 关键引用:
- "It might be more useful with a signature like maybedivide -> maybe(int) -> maybe(int) -> maybe(int)."
- "使用maybedivide -> maybe(int) -> maybe(int) -> maybe(int)这样的签名可能更有用。"
对创意的赞赏
- 主要观点:评论者赞赏作者在C语言中实现高级抽象的创意。
- 关键引用:
- "I love seeing the creative ways people implement these types of, if I can say, high level abstractions in C."
- "我喜欢看到人们在C语言中实现这些高级抽象的创意方式。"
对实现目标的批评
- 主要观点:评论者指出该实现未能强制用户检查结果,建议通过完全隐藏maybe(T)的内部结构,并通过宏来强制检查和访问。
- 关键引用:
- "Clever, but it misses the very goal of option/maybe types: forcing the user to check the result."
- "聪明,但它忽略了option/maybe类型的核心目标:强制用户检查结果。"