C 语言没有 C++、Java 等语言中的异常机制;库函数与系统调用通常通过返回值表示成败,并在失败时设置全局变量 errno。结合条件判断与 perror、strerror 等函数,可编写可维护的错误处理逻辑。文件相关 API 的用法见文件处理一节。

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(void) {
    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. 检查返回值

对返回指针的函数判断是否为 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(void) {
    FILE* fp = fopen("test.txt", "r");

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

3. strerror()

strerror 返回指向错误描述字符串的指针,便于自行格式化输出:

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

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

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

4. ferror()

在写操作后检查流是否出错:

#include <stdio.h>

int main(void) {
    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(void) {
    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 或错误标志;clearerr 可清除这些标志以便继续操作:

#include <stdio.h>

int main(void) {
    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;
}

进程退出状态:exit()

exit 向环境返回状态码;EXIT_SUCCESS 与 EXIT_FAILURE 为可移植的宏:

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

int main(void) {
    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 后,同一函数内后续语句不会执行。

应用层前置检查

除零等逻辑错误可在调用库函数之前用条件判断拦截:

#include <stdio.h>

int main(void) {
    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;
}

小结

健壮程序应检查关键函数返回值,在失败时读取 errno 或流错误标志,并选择记录日志、清理资源或终止进程。多步资源获取失败时,可结合goto 统一清理避免重复代码。

本节归纳了 errno 与常见 I/O 错误检测函数。下一节说明变参函数与 stdarg.h 的用法。