Skip to content

[English version](/posts/c教程/en-US/05-Input Output and Files/046-Error Checking with errno and perror)

即使写得最好的 C 程序也可能会遇到错误、文件丢失、权限问题、被零除或内存分配失败。与某些引发异常的语言不同,C 使用返回值和名为的全局变量手动报告错误errno.

了解如何使用errno,perror(), 和strerror()对于编写能够优雅且信息丰富地失败的健壮的生产级 C 程序至关重要。

errno 背后的想法 ​

errno是一个全局整数(在<errno.h>)每当库函数失败时存储错误代码。

  • 成功后,大多数功能都会离开errno不变。
  • 一旦失败,他们通常会设置errno并返回一个错误值(通常NULL,-1, 或者0取决于功能)。
  • 必须先检查返回值,然后才应该检查errno.

例子:

#include <stdio.h>
#include <errno.h>
int main(void) {
    FILE *fp = fopen("missing.txt", "r");
    if (!fp) {
        printf("Error code: %d\n", errno);
        perror("fopen failed");
    }
    return 0;
}

输出:

Error code: 2
fopen failed: No such file or directory

常见错误代码 ​

代码名称意义
2ENOENT没有这样的文件或目录
13EACCES权限被拒绝
12ENOMEM内存不够
22EINVAL无效参数
17EEXIST文件已存在
5EIO输入/输出错误
111ECONNREFUSED连接被拒绝(网络)

包括<errno.h>使用这些符号名称:

if (errno == ENOENT) printf("File missing.\n");

使用 perror() ​

perror()打印一条人类可读的错误消息stderr,基于当前值errno.

perror("File open error");

例子:

#include <stdio.h>
#include <errno.h>
int main(void) {
    FILE *f = fopen("ghost.txt", "r");
    if (!f)
        perror("Unable to open file");
    return 0;
}

输出:

Unable to open file: No such file or directory

使用 strerror() ​

如果您想在自己的格式化输出中使用错误消息,请使用strerror()从<string.h>:

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void) {
    FILE *f = fopen("/root/secret.txt", "r");
    if (!f)
        printf("Error (%d): %s\n", errno, strerror(errno));
    return 0;
}

输出:

Error (13): Permission denied

小代码:强大的文件阅读器 ​

这是一个简单的文件读取器,可以在每一步检查错误:

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 1;
    }
    FILE *fp = fopen(argv[1], "r");
    if (!fp) {
        fprintf(stderr, "Error opening %s: %s\n", argv[1], strerror(errno));
        return 1;
    }
    char buf[128];
    while (fgets(buf, sizeof(buf), fp))
        printf("%s", buf);
    if (ferror(fp)) {
        fprintf(stderr, "Error reading file: %s\n", strerror(errno));
    }
    fclose(fp);
    return 0;
}

用法:

./readfile poem.txt

如果文件丢失:

Error opening poem.txt: No such file or directory

清除并重置errno ​

某些功能可以设置errno即使他们后来成功了。为了安全起见,您可以在调用之前清除它:

#include <errno.h>
errno = 0;
FILE *f = fopen("file.txt", "r");
if (!f) perror("fopen");

这可确保您不会读取先前操作中遗留的错误。

检查其他系统错误 ​

errno不限于文件 I/O,它适用于许多系统调用和库函数:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void) {
    FILE *f = fopen("/dev/full", "w"); // special Linux device that fails on write
    if (f) {
        if (fputc('A', f) == EOF)
            perror("Write failed");
        fclose(f);
    }
    return 0;
}

输出:

Write failed: No space left on device

为什么它很重要 ​

errno及其助手(perror,strerror)让你的程序在出现问题时能够自我解释。这对于以下方面至关重要:

  • 必须报告特定错误原因的系统工具
  • 调试生产代码
  • 编写可移植、可维护的程序

优秀的 C 开发人员绝不会“默默地失败”。

自己尝试一下 ​

  1. 打开一个不存在的文件,然后创建它并重试。
  2. 使用以下命令模拟读取错误ferror()读取关闭的文件后。
  3. 尝试写入目录(fopen("/tmp", "w"))并检查errno.
  4. 使用循环打印所有已知的错误代码和消息strerror().
  5. 编写一个小的“safe_open”函数来包装fopen与错误报告。

掌握错误处理后,您现在知道如何使 C 程序既丰富又可靠。接下来,您将探索命令行参数(argc,argv),构建灵活的、可编写脚本的工具来动态处理用户输入的门户。

本站总访问 — 次