Linux 错误处理:perror 与 strerror 实战指南
80T资源合集下载
链接:https://pan.quark.cn/s/5643428d4f9f
在编程世界里,我们都喜欢一帆风顺。但现实是,程序总会因为各种原因出错:文件不存在、权限不足、磁盘已满……一个只会默默失败的程序是可怕的,它让你像个无头苍蝇一样去猜错因。而一个专业的程序,则应该在失败时清晰地“告诉”你,它到底遭遇了什么。
今天,我们就来学习如何让我们的 C 程序“学会说话”,掌握 Linux 提供的两大错误处理利器:perror 和 strerror。
一、沉默的失败者:一个反面教材
让我们从一个简单的文件拷贝程序开始。这个版本只做了最基础的返回值检查,如果出错,它只会打印一句含糊不清的“Error!”。
// naive_cp.c (一个不擅长表达的程序)
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Usage: %s <source> <destination>\n", argv[0]);
exit(1);
}
int fd_src = open(argv[1], O_RDONLY);
if (fd_src == -1) {
printf("Error opening source file!\n"); // 太模糊了!
exit(1);
}
// ... 后续读写逻辑 ...
close(fd_src);
printf("Operation might be successful...?\n");
return 0;
}
现在,让我们故意制造一些错误场景:
场景 1:源文件不存在
gcc naive_cp.c -o naive_cp
./naive_cp non_existent_file.txt out.txt
运行结果:
Error opening source file!
它只告诉我们错了,但为什么错?是文件不存在?还是权限问题?我们无从得知。
二、神秘的信使:全局变量 errno
当 Linux 的系统调用(如 open, read)执行失败时,它们除了返回 -1 之外,还会做一件非常重要的事情:将一个全局整型变量 errno 设置为一个特定的值,用以表示具体的错误类型。
-
errno:定义在 <errno.h> 头文件中。 - 错误码:每一个
errno 的值都对应一个宏定义,如 ENOENT (No such file or directory)、EACCES (Permission denied) 等。
errno 就是内核传递给我们的“错误情报”。现在的问题是,我们如何解读这份情报?
三、利器一:perror —— 简单直接的错误播报员
perror 是最简单、最直接的错误报告工具。
- 函数原型:
void perror(const char *s); - 头文件:
<stdio.h> - 工作方式:
- 它会先打印你传入的字符串
s。 - 然后自动加上一个冒号和一个空格
": "。 - 最后,它会根据当前
errno 的值,打印出对应的、人类可读的错误描述信息。 - 所有内容都输出到标准错误流 (stderr)。
升级我们的程序 (perror_cp.c):
// perror_cp.c (学会使用 perror)
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h> // 虽然 perror 会自动用,但明确包含是个好习惯
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
exit(1);
}
int fd_src = open(argv[1], O_RDONLY);
if (fd_src == -1) {
// 使用 perror!
perror("open source file failed");
exit(1);
}
printf("Source file opened successfully, fd = %d\n", fd_src);
close(fd_src);
return 0;
}
再次进行错误场景测试:
场景 1:源文件不存在
gcc perror_cp.c -o perror_cp
./perror_cp non_existent_file.txt out.txt
运行结果:
open source file failed: No such file or directory
看!信息变得无比清晰!我们自定义的 "open source file failed" 后面,perror 自动追加了根本原因:"No such file or directory"。
场景 2:没有权限读取文件
touch secret.txt
chmod 000 secret.txt # 移除所有权限
./perror_cp secret.txt out.txt
运行结果:
open source file failed: Permission denied
同样精准地指出了是“权限被拒绝”的问题。
四、利器二:strerror —— 灵活定制的错误翻译家
perror 很方便,但它只能直接打印到 stderr。如果我们想把错误信息记录到日志文件,或者格式化成 JSON 发送到监控系统呢?这时,strerror 就派上用场了。
- 函数原型:
char *strerror(int errnum); - 头文件:
<string.h> - 工作方式: 你将
errno 的值作为参数传给它,它会返回一个指向错误描述字符串的指针。
使用 strerror 进一步升级程序 (strerror_cp.c):
// strerror_cp.c (学会使用 strerror)
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> // for strerror
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
exit(1);
}
int fd_src = open(argv[1], O_RDONLY);
if (fd_src == -1) {
// 使用 strerror,配合 fprintf 灵活输出
fprintf(stderr, "[FATAL ERROR] Failed to open '%s'. Reason: %s (errno: %d)\n",
argv[1], strerror(errno), errno);
exit(1);
}
printf("Source file '%s' opened successfully, fd = %d\n", argv[1], fd_src);
close(fd_src);
return 0;
}
进行同样的错误场景测试:
场景 1:源文件不存在
gcc strerror_cp.c -o strerror_cp
./strerror_cp non_existent_file.txt out.txt
运行结果:
[FATAL ERROR] Failed to open 'non_existent_file.txt'. Reason: No such file or directory (errno: 2)
我们不仅得到了错误描述,还完全自定义了输出格式,甚至打印出了原始的 errno 值 (2 对应 ENOENT),这对于调试和日志记录非常有用。
五、perror vs. strerror:如何选择?
|
特性 |
|
|
|
功能 |
打印自定义前缀 + 错误描述 |
返回错误描述字符串 |
|
输出位置 |
固定输出到 |
由你决定( |
|
易用性 |
非常简单,一行搞定 |
稍复杂,需要配合 |
|
适用场景 |
简单的命令行工具,快速诊断 |
需要灵活格式化、记录日志、GUI 弹窗等复杂场景 |
一句话总结:想快速在终端看错误,用 perror;想对错误信息做进一步处理,用 strerror。
结语
编写健壮的程序,就像建造一栋坚固的建筑,地基必须扎实。在 Linux 系统编程中,对每一个可能失败的系统调用进行返回值检查,并使用 perror 或 strerror 清晰地报告错误,就是这个不可或缺的地基。

更多推荐



所有评论(0)