C 标准库之 <errno.h> 详解与深度解析

关键词:C语言、错误类型、错误码、errno

适用读者:系统编程人员、嵌入式开发工程师、Linux 后端开发人员、内核/驱动爱好者


在 C 语言编程中,错误处理 一直是程序员必须面对的重要问题。C 标准库提供了 <errno.h> 头文件,它定义了一系列用于报告和处理运行时错误的机制。本文将系统讲解 <errno.h> 的作用、常用宏、使用方法及注意事项,并配以实例和图示,帮助你在编程中更加高效地处理错误。


<errno.h> 简介

<errno.h> 提供了一种标准化的方式来报告程序运行时错误。

  • 定义全局变量 errno,用于存储最近发生的错误代码。
  • 系统调用或库函数在失败时会设置 errno 来指示错误类型。
  • 定义了大量宏来表示不同错误类型,如 EPERMENOENTEIO

注意:errno 并不是函数返回值,它是库函数通过全局变量记录的错误码。


二、errno 变量详解

c 复制代码
extern int errno;
  • 类型:int
  • 作用:保存最近一次系统调用或库函数出错的错误码
  • 特点:
    • 线程安全 :在多线程程序中,每个线程的 errno 是独立的(通过 TLS 实现)。
    • 初始值 :成功调用函数通常不会修改 errno,调用前可设为 0。
    • 跨平台注意:不同操作系统可能定义额外错误码,不要依赖特定数值。

errno 触发机制示意图

yaml 复制代码
												系统调用/库函数执行
												        |
												        v
												   函数返回值检查
												        |
												   成功? ------→ 不修改 errno
												        |
												        v
												   失败
												        |
												   设置 errno
												        |
												        v
												   用户获取 errno
												        |
												   strerro/perror 输出

三、常见错误码及含义

下面是 <errno.h> 中常用的错误码:

错误码 描述
EPERM 操作不允许
ENOENT 没有这样的文件或目录
ESRCH 没有这样的进程
EINTR 系统调用被中断
EIO 输入/输出错误
ENXIO 没有这样的设备或地址
E2BIG 参数列表太长
ENOMEM 内存不足
EACCES 权限被拒绝
EFAULT 坏的地址
EBUSY 资源忙
EEXIST 文件已存在
EXDEV 跨设备链接
ENODEV 没有这样的设备
ENOTDIR 不是一个目录
EISDIR 是一个目录
EINVAL 无效的参数
ENFILE 系统文件表溢出
EMFILE 打开的文件过多
ENOTTY 不是终端设备
ETXTBSY 文本文件忙
EFBIG 文件过大
ENOSPC 设备上没有空间
ESPIPE 非法寻址
EROFS 只读文件系统
EMLINK 链接过多
EPIPE 管道破裂

Tip:C 程序中可根据不同 errno 进行针对性处理,提高程序鲁棒性。


四、使用 errno 的示例

4.1 文件操作

c 复制代码
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
    FILE *file = fopen("nonexistent_file.txt", "r");
    if (!file) {
        printf("Error opening file: %s (errno=%d)\n", strerror(errno), errno);
        return 1;
    }
    fclose(file);
    return 0;
}

输出示例:

yaml 复制代码
Error opening file: No such file or directory (errno=2)

4.2 数学函数错误

c 复制代码
#include <stdio.h>
#include <math.h>
#include <errno.h>

int main() {
    errno = 0; // 调用前清零
    double val = sqrt(-1.0); // 不合法的平方根
    if (errno == EDOM) {
        printf("Math error: domain error\n");
    }
    return 0;
}

4.3 系统调用错误

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

int main() {
    int fd = open("/root/secret_file.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file"); // perror 自动打印 errno 对应的错误信息
    }
    return 0;
}

五、errno 高级实践

  1. 多线程安全使用
    不要使用全局变量 errno 共享状态,每个线程都有独立的 errno
  2. 日志记录和调试
    结合 strerror(errno) 输出到日志中,可快速定位错误原因。
  3. 函数调用前设为 0
    避免函数调用前 errno 已有历史值,造成误判。
  4. 跨平台兼容性
    避免直接依赖 errno 的数值,使用宏名称判断错误类型。

六、总结

  • <errno.h> 提供标准化错误处理机制,使 C 程序能够准确报告运行时错误。
  • errno 是全局整数变量,保存最近一次错误码。
  • 常用宏(如 ENOENTEIOEDOM 等)表示不同错误类型。
  • 结合 strerrorperror 输出,可快速调试和记录错误。
  • 正确使用 errno 能显著提升程序鲁棒性和可维护性。
相关推荐
炘爚5 分钟前
C++ 右值引用与程序优化
开发语言·c++
si莉亚21 分钟前
ROS2安装EVO工具包
linux·开发语言·c++·开源
清心歌29 分钟前
CopyOnWriteArrayList 实现原理
java·开发语言
良木生香1 小时前
【C++初阶】C++入门相关知识(2):输入输出 & 缺省参数 & 函数重载
开发语言·c++
忘梓.1 小时前
墨色规则与血色节点:C++红黑树设计与实现探秘
java·开发语言·c++
hhh3u3u3u1 小时前
Visual C++ 6.0中文版安装包下载教程及win11安装教程
java·c语言·开发语言·c++·python·c#·vc-1
星河耀银海1 小时前
C++ 模板进阶:特化、萃取与可变参数模板
java·开发语言·c++
泛凡(Linyongui)1 小时前
PY32F002B实践之二--宠物腹背理疗仪项目介绍及头文件解析
c语言·keil·py32·32位单片机·腹背理疗仪项目实践
cccccc语言我来了1 小时前
【C++---unordered_set/map底层封装】个不拘一格的集合。它不似有序集合那般循规蹈矩,而是以一种洒脱不羁的方式,将元素们随意地散落其中。每一个元素都是独一无二的。
开发语言·c++·哈希算法
Zfox_1 小时前
C++ IO流全解析:标准库中的数据处理与文件读写艺术
开发语言·c++