1. errno
:错误代码变量
errno
是一个全局变量(实际上是线程局部存储变量),定义在头文件 <errno.h>
中,用于存储上一次函数调用发生的错误代码。C标准库中很多函数在发生错误时会设置 errno
,以便程序可以进一步检查错误的详细信息。
特点:
- 初始值:
errno
的初始值是 0。如果一个函数调用成功,它通常不会修改errno
。 - 错误代码:
errno
的值对应不同的错误类型,例如:EINVAL
:无效参数ENOENT
:没有找到文件或目录EACCES
:权限被拒绝
- 需要注意的是,只有在函数调用明确出错时,
errno
的值才有意义。
2. strerror
:将错误代码转换为描述性字符串
strerror
函数用于将错误代码(例如 errno
的值)转换为人类可读的字符串描述。
参数:
errnum
:错误代码(通常为errno
的值)。
返回值:
- 返回一个指向静态字符串的指针,该字符串描述了错误代码。
特别注意:
strerror
返回的字符串由库管理,用户不需要释放,但其内容可能会在后续调用中被覆盖。
3. perror
:打印错误信息
perror
函数直接将错误信息打印到标准错误流(stderr
)。它结合 errno
的值打印一条描述性消息,通常用于快速调试。
参数:
s
:一个用户定义的字符串,通常是发生错误的函数名称或上下文描述。如果传入NULL
,仅输出错误描述。
三者的对比与应用场景
函数 | 功能 | 参数及返回值 | 应用场景 |
---|---|---|---|
errno | 储存错误代码 | 全局变量,存储错误类型代码 | 检查函数是否出错以及出错类型。 |
strerror | 将错误代码转换为描述性字符串 | 输入错误代码,返回描述性字符串 | 在需要更详细或可读的错误信息时使用。 |
perror | 打印错误信息到标准错误流 | 输入用户字符串,输出格式化错误消息 | 快速调试和直接打印错误消息。 |
综合示例
以下是一个结合使用 errno
、strerror
和 perror
的综合示例:
#include <stdio.h>
#include <errno.h>
#include <string.h>int main() {FILE *file = fopen("nonexistent.txt", "r");if (file == NULL) {// 使用 errno 输出错误代码printf("Error code: %d\n", errno);// 使用 strerror 转换错误信息printf("Error description: %s\n", strerror(errno));// 使用 perror 打印详细错误信息perror("File open failed");}return 0;
}
C语言中 errno
、strerror
和 perror
的注意事项
1. errno
的注意事项
-
只在错误发生后读取
errno
的值:- 仅当标准库函数调用失败时,
errno
的值才有意义。 - 调用成功的函数通常不会修改
errno
,因此在成功调用后,不应依赖errno
的值。
- 仅当标准库函数调用失败时,
-
errno
是线程局部存储:- 在多线程程序中,每个线程有自己的
errno
,这保证了线程安全。
- 在多线程程序中,每个线程有自己的
-
初始化
errno
为 0:- 在调用某些函数前,如果需要明确判断是否出错,应该将
errno
设置为 0。
- 在调用某些函数前,如果需要明确判断是否出错,应该将
-
不要对
errno
硬编码判断:- 应该使用标准头文件
<errno.h>
中定义的宏(如EACCES
,EINVAL
),而不是直接比较错误代码值。
- 应该使用标准头文件
-
errno
可能被覆盖:- 某些库函数可能会修改
errno
的值,因此在多个函数调用中间,如果需要保留errno
的值,应该在第一时间保存。
- 某些库函数可能会修改
2. strerror
的注意事项
-
返回值是静态字符串:
strerror
返回的字符串是静态分配的,不能被用户修改或释放。- 多次调用可能会覆盖之前返回的字符串。
-
线程安全问题(C99 之前):
- 在某些旧的 C 标准中,
strerror
是非线程安全的,因为它返回的是静态字符串。 - 如果需要线程安全的版本,可以使用 POSIX 提供的
strerror_r
(取决于实现)。
- 在某些旧的 C 标准中,
-
错误代码的合法性:
- 传递给
strerror
的错误代码(errnum
)必须是合法的。如果是未定义的错误代码,返回值可能是未指定的字符串。
- 传递给
-
语言支持问题:
strerror
返回的错误描述可能根据系统语言环境而变化,程序运行时需要考虑本地化问题。
3. perror
的注意事项
-
直接输出到标准错误流:
perror
的输出目标是标准错误流(stderr
),不能直接捕获到标准输出流(stdout
)。- 如果需要捕获或重定向错误信息,可以改用
strerror
格式化后输出。
-
打印时附加前缀字符串:
perror
的参数(s
)是用户定义的上下文描述字符串。为了清晰的错误日志,应传入适当的上下文信息。- 如果传入
NULL
,只输出错误信息。
-
用于调试而非用户交互:
perror
打印的错误信息对开发者非常有帮助,但它的格式不适合作为用户提示。- 在用户交互中,建议用
strerror
构造更友好的错误消息。
-
与多线程的关系:
perror
会输出当前线程的errno
对应的错误信息,适合在多线程环境下调试。
-
不可直接修改
errno
:perror
依赖于errno
的值,因此在调用perror
之前,不要显式修改errno
。