C 没有 C++/Java 那套 try-catch 异常机制,错误处理全靠"函数的返回值 + 全局 errno"。这套路看着原始,但学会之后,任何 C 项目的错误排查都有章法可循。

errno:出错时的那串错误码

errno 是 errno.h 里定义的全局变量。标准库函数一失败,就把对应的错误码存进去。最常用的几个:

errno含义
1Operation not permitted(操作不允许)
2No such file or directory(文件不存在)
5I/O error
12Out of memory(内存不足)
13Permission denied(权限不足)

最基本的用法,打开不存在的文件:

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

int main() {
    FILE *fp = fopen("missing.txt", "r");

    if (fp == NULL) {
        printf("Error Code: %d\n", errno);
        perror("Error");
    }
    return 0;
}

输出:

Error Code: 2
Error: No such file or directory

几种常用的错误处理手段

1. if-else 判断返回值

最基础也最常见。函数返回 NULL / -1 就是出错信号:

FILE* fp;
fp = fopen("test.txt", "r");

if (fp == NULL) {
    printf("File opening error\n");
} else {
    printf("File open successfully\n");
}

2. perror():打印错误描述

perror() 把你自己写的消息和 errno 对应的描述拼一起输出到标准错误流:

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

int main() {
    FILE* fp = fopen("test.txt", "r");

    printf("Value of errno: %d\n", errno);
    perror("Message from perror");
    return 0;
}

输出:

Value of errno: 2
Message from perror: No such file or directory

3. strerror():拿到错误描述字符串

strerror() 把 errno 转成可读的文本,方便自己格式化输出:

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

int main() {
    FILE* fp = fopen("test.txt", "r");

    printf("Value of errno: %d\n", errno);
    printf("The error message is : %s\n", strerror(errno));
    return 0;
}

输出:

Value of errno: 2
The error message is : No such file or directory

4. ferror():检查文件操作是否出错

写文件后查一下有没有出错:

#include <stdio.h>

int main() {
    FILE *fptr = fopen("test.txt", "w");
    if (fptr == NULL) return 1;

    fprintf(fptr, "Hello, online-compiler!");

    if (ferror(fptr) == 0)
        printf("Data written successfully.\n");
    fclose(fptr);
    return 0;
}

5. feof():判断是否读到文件尾

读文件时判断到没到结尾,避免越读越出错:

#include <stdio.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL)
        return 0;

    do {
        char c = fgetc(fp);
        if (feof(fp))
            break;
        printf("%c", c);
    } while (1);

    fclose(fp);
    return 0;
}

6. clearerr():清掉 EOF / 错误标记

文件读到 EOF 后,feof 标记会一直挂着。clearerr() 能清掉,让流可以继续用:

#include <stdio.h>

int main() {
    FILE *fptr = fopen("test.txt", "w+");
    fprintf(fptr, "Hello, online-compiler!");
    while (fgetc(fptr) != EOF);

    if (feof(fptr)) {
        printf("EOF encounter\n");
    }

    clearerr(fptr);
    if (!feof(fptr)) {
        printf("Reset the EOF successfully\n");
    }

    fclose(fptr);
    return 0;
}

输出:

EOF encounter
Reset the EOF successfully

退出状态:exit() 告诉系统成没成

exit() 能把程序状态码传给操作系统。标准定义了两个常量:EXIT_SUCCESS 和 EXIT_FAILURE。

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

int main() {
    FILE* fp = fopen("test.txt", "rb");

    if (fp == NULL) {
        printf("Value of errno: %d\n", errno);
        printf("Error opening the file: %s\n", strerror(errno));
        perror("Error printed by perror");

        exit(EXIT_FAILURE);   /* 失败退出 */

        printf("I will not be printed\n");   /* 根本不会执行 */
    } else {
        fclose(fp);
        exit(EXIT_SUCCESS);   /* 成功退出 */
    }
    return 0;
}

注意 exit() 一调用,后面代码直接不跑了,别在 exit 后面写依赖执行的逻辑。

不靠函数也能防的错

很多错误压根不用走到标准库,写代码时就提前拦下:

#include <stdio.h>

int main() {
    int num1 = 10, num2 = 0;

    if (num2 == 0)
        printf("Error: Division by zero is not allowed\n");
    else
        printf("Result: %d", num1 / num2);
    return 0;
}

输出:

Error: Division by zero is not allowed

除零、越界、非法输入这类,用 if 提前判断,程序根本不会走到崩溃那一步。

套路总结

C 语言的错误处理可以归成三句话:函数返回值告诉你成没成,errno 告诉你为什么,if/perror/strerror 把这两件事讲给人听。写健壮代码,就是每一步都检查、每一条错误路径都交代清楚。