Z
ZHANK
高级技巧

错误处理

errno/perror、返回错误码、assert 断言、setjmp/longjmp

错误处理

C 没有异常机制——错误通过返回值、errno、assert 来传达。处理不好直接崩溃或行为异常。

学完本章你将: 掌握 errno/perror、返回错误码、assert 断言、setjmp/longjmp。


errno —— 全局错误码

c
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
    FILE *fp = fopen("不存在.txt", "r");
    if (fp == NULL) {
        printf("错误码:%d\n", errno);
        printf("错误信息:%s\n", strerror(errno));
        perror("fopen");  // 打印 "fopen: No such file or directory"
        return 1;
    }
    fclose(fp);
    return 0;
}

返回错误码

c
// ✅ 用返回值表示成功/失败
int divide(int a, int b, int *result) {
    if (b == 0) return -1;  // 错误
    *result = a / b;
    return 0;                // 成功
}

// 调用
int result;
if (divide(10, 0, &result) != 0) {
    printf("除数不能为零!\n");
}

assert —— 调试时的安全检查

c
#include <assert.h>

int factorial(int n) {
    assert(n >= 0);   // 调试时检查条件,发布时可禁用
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

💡 assert 只在未定义 NDEBUG 时生效。发布版本通常会定义 NDEBUG 来禁用所有 assert。


setjmp/longjmp —— 类似异常

c
#include <setjmp.h>

jmp_buf buf;

void deepFunction() {
    printf("出错,跳回\n");
    longjmp(buf, 1);  // 跳回 setjmp 处
    printf("这行永远不会执行\n");
}

int main() {
    if (setjmp(buf) == 0) {
        printf("进入深层函数\n");
        deepFunction();
    } else {
        printf("捕获到跳转\n");
    }
    return 0;
}

⚠️ setjmp/longjmp 是最后的底牌——它跳过正常的栈展开,可能导致资源泄漏。优先用返回值处理错误。