Skip to content
📖 Auto 版 Modern C本书目录

第十五章:程序失败 ​

级别 2 — 认知

未定义行为、错误码、信号——C 中如何出错以及 Auto 如何防止它们。

程序会失败。问题不在于失败是否会发生,而在于语言如何帮助你预防、检测和恢复。C 的失败处理方法是最小化的:语言假设程序员总是正确的,并且很少提供安全网。Auto 采取相反的方法——语言通过编译时检查和运行时安全帮助程序员避免失败。


15.1 不当行为 ​

C 将程序错误行为分为几个类别。最危险的是未定义行为(UB):

未定义行为:标准不对其施加任何要求的行为。

UB 意味着编译器被允许做任何事情。常见示例:

c
// C 深入:未定义行为
int arr[5];
arr[10] = 42;           // UB:越界访问

int *p = NULL;
*p = 42;                // UB:空指针解引用

int x;
printf("%d\n", x);      // UB:读取未初始化变量

int a = INT_MAX;
int b = a + 1;          // UB:有符号整数溢出

free(p);
*p = 42;                // UB:释放后使用

UB 在实践中实际发生的情况:

场景可能的后果
缓冲区溢出静默数据损坏
空指针解引用段错误(崩溃)
释放后使用任意代码执行
整数溢出不正确的结果
类型混淆数据损坏,安全漏洞

编译器利用 UB 进行优化。如果标准说行为未定义,编译器可以假设它永远不会发生:

c
// C 深入:编译器利用 UB
int *check(int *p) {
    int x = *p;        // 解引用 p
    if (p == NULL) {   // 编译器移除此检查:如果 p 是 NULL,
        return NULL;   // 上面的解引用就是 UB,
    }                   // 而 UB "不会发生"
    return p;
}

Auto 如何防止 UB。 Auto 通过以下方式消除整类 UB:

  • 边界检查:数组访问在可能时在编译时验证,在必要时在运行时验证。
  • 空值安全:Auto 中没有空指针。可选值使用显式 Option 类型。
  • 初始化强制:每个变量必须初始化。
  • 无指针算术:数组索引替代所有指针算术。
  • 溢出检测:整数溢出在运行时被捕获。
auto
// Auto:UB 预防
fn safe_divide(a int, b int) int {
    if b == 0 {
        print("Error: division by zero")
        return 0
    }
    a / b
}

fn safe_access(arr [5]int, index int) int {
    if index < 0 || index >= 5 {
        print("Error: index out of bounds")
        return 0
    }
    arr[index]
}

基于 MIT 许可发布。