C语言 标准库
错误处理机制详解 – 编程小白必读指南
📌 什么是errno.h?
errno.h 是C标准库中用于错误处理的头文件。当调用库函数出错时,系统会设置一个全局变量 errno,通过这个变量可以了解发生了什么错误。
简单理解:就像打电话时如果对方没接,手机会告诉你”对方暂时无法接通”或”对方正在通话中”。errno.h 就是告诉你程序哪里出问题的”错误提示系统”。
🔧 errno是什么?
errno 是一个全局整型变量,用于存储最近发生的错误代码。当一个库函数执行失败时,它会设置 errno 的值,表明发生了什么错误。
重要提示:只有在函数明确指明失败时才会设置 errno!成功执行的函数不会修改 errno。
✅ 如何使用errno?
- 调用可能失败的库函数(如 fopen()、malloc() 等)
- 检查函数返回值是否表示失败
- 如果失败,立即检查 errno 的值
- 根据 errno 的值进行错误处理
常见错误宏定义
错误宏 | 值 | 含义 | 常见场景 |
---|---|---|---|
EPERM | 1 | 操作不允许(权限不足) | 尝试修改只读文件 |
ENOENT | 2 | 文件或目录不存在 | 打开不存在的文件 |
EINTR | 4 | 系统调用被中断 | 等待用户输入时按下Ctrl+C |
EIO | 5 | 输入/输出错误 | 读取损坏的磁盘 |
EBADF | 9 | 错误的文件描述符 | 使用了未打开的文件描述符 |
EAGAIN | 11 | 资源暂时不可用 | 非阻塞操作无法立即完成 |
ENOMEM | 12 | 内存不足 | malloc失败 |
EACCES | 13 | 权限不足 | 尝试打开无权限的文件 |
EFAULT | 14 | 错误的地址 | 传递了无效指针 |
EEXIST | 17 | 文件已存在 | 创建已存在的文件 |
EINVAL | 22 | 无效参数 | 传递了无效参数给函数 |
ENOSPC | 28 | 设备无空间 | 磁盘空间不足 |
错误处理函数
📝 perror()
打印错误信息到标准错误输出(stderr)
#include <stdio.h> void perror(const char *s);
参数:s – 自定义的错误消息前缀
示例:perror(“文件打开失败”);
输出:文件打开失败: No such file or directory
📝 strerror()
返回错误代码对应的字符串描述
#include <string.h> char *strerror(int errnum);
参数:errnum – 错误代码(errno的值)
返回值:错误描述的字符串
示例:printf(“错误: %s”, strerror(errno));
完整代码示例
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *fp;
// 尝试打开一个不存在的文件
fp = fopen("nonexistent.txt", "r");
if (fp == NULL) {
printf("错误代码: %d\n", errno);
// 使用 perror 打印错误信息
perror("文件打开失败");
// 使用 strerror 获取错误信息
printf("错误信息: %s\n", strerror(errno));
// 根据不同错误类型处理
switch(errno) {
case ENOENT:
printf("处理:文件不存在,请检查路径\n");
break;
case EACCES:
printf("处理:权限不足,请检查文件权限\n");
break;
default:
printf("处理:未知错误,代码 %d\n", errno);
}
return 1;
}
// 文件操作...
fclose(fp);
return 0;
}
示例输出:
错误代码: 2
文件打开失败: No such file or directory
错误信息: No such file or directory
处理:文件不存在,请检查路径
注意事项
⚠️ 陷阱警告
- 成功执行的函数不会重置errno为0
- 检查errno前必须确认函数执行失败
- 库函数调用可能覆盖errno的值
- 在多线程程序中,errno是线程安全的(每个线程有自己的副本)
- 程序启动时errno初始值为0
✅ 最佳实践
- 在调用可能失败函数后立即检查errno
- 使用perror()或strerror()输出错误信息
- 处理错误时考虑恢复机制或安全退出
- 不要假设errno的值,总是检查具体错误
- 复杂的程序建议自定义错误处理函数
为什么errno是一个宏?
在大多数实现中,errno不是一个真正的全局变量,而是一个宏。这是为了支持多线程环境。每个线程都有自己的errno副本,防止一个线程的错误覆盖另一个线程的错误。