C 语言的错误处理(Error Handling)机制
本文将从实战、标准规范、语言机制和库支持等角度,全面系统地讲解 C 语言的错误处理(Error Handling)机制。
📘 一、C 语言错误处理概述
C 语言不像 Java、Python 有内建的异常机制(如 try-catch),但它提供了多种错误处理手段,包括:
| 方式 | 简介 |
|---|---|
| 返回码机制 | 函数通过返回值传达成功/失败 |
errno 全局变量 | 由标准库函数设置,表示错误类型 |
perror() 和 strerror() | 打印或解释错误 |
| 自定义错误处理函数 | 用户根据返回码做判断 |
setjmp() 和 longjmp() | 实现异常跳转(复杂、慎用) |
| 信号(signal)机制 | 处理运行时错误(如除零、段错误) |
📂 二、返回码机制(Return Code)
✅ 通用函数返回值判断
int result = some_function();
if (result != 0) {
// 错误处理逻辑
}
例:fopen 打开文件失败时返回 NULL
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("Failed to open file");
}
📌 三、errno 错误码机制
errno 是 <errno.h> 中定义的全局变量,由许多标准库函数在出错时设置。
常见 errno 值(来自 POSIX 标准)
| 宏常量 | 错误码 | 含义 |
|---|---|---|
EPERM | 1 | 操作不被允许 |
ENOENT | 2 | 文件或目录不存在 |
EIO | 5 | I/O 错误 |
ENOMEM | 12 | 内存不足 |
EACCES | 13 | 权限被拒绝 |
EINVAL | 22 | 参数无效 |
示例:
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *fp = fopen("no_such_file.txt", "r");
if (!fp) {
printf("errno: %d\n", errno);
printf("Error: %s\n", strerror(errno)); // 或用 perror()
}
return 0;
}
🔍 四、perror() 与 strerror()
perror(const char *msg)
输出形式为:
msg: 错误信息
perror("fopen failed");
strerror(int errnum)
返回错误码对应的字符串
printf("Error: %s\n", strerror(errno));
⚠️ 五、setjmp() 和 longjmp() —— C 中的“异常跳转”
来自 <setjmp.h>,可以用来模拟“异常处理结构”,但结构复杂、跳转混乱,慎用。
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
void my_function() {
printf("Throwing error!\n");
longjmp(buf, 1);
}
int main() {
if (setjmp(buf)) {
printf("Error caught via longjmp\n");
} else {
my_function();
}
return 0;
}
🔁 类似 try 和 throw,但不推荐用于日常代码,尤其是在复杂项目中。
🚦 六、信号机制(signal)处理运行时错误
来自 <signal.h>,例如处理除零、段错误、终止信号等。
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void handler(int sig) {
printf("Caught signal: %d\n", sig);
exit(1);
}
int main() {
signal(SIGFPE, handler); // 捕获除零错误
int x = 1 / 0; // 除零,触发 SIGFPE
return 0;
}
🧠 七、专业建议:如何设计你的错误处理逻辑?
- 封装函数返回状态码(建议使用 enum)
- 封装错误日志记录(如
log_error函数) - 避免使用 setjmp/longjmp,维护困难
- 合理使用 errno,但不要依赖其“准确性”
- 对所有资源访问(内存、文件、网络)进行判空检查
- 必要时统一错误处理机制,提升可维护性
🧪 八、实战示例:文件读取 + 错误检查
#include <stdio.h>
#include <errno.h>
#include <string.h>
int read_file(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
fprintf(stderr, "Error opening file '%s': %s\n", filename, strerror(errno));
return errno;
}
// ... 文件操作 ...
fclose(fp);
return 0;
}
📚 九、权威资料 & 出站链接
| 类型 | 链接/来源 |
|---|---|
C 标准头文件 <errno.h> | https://en.cppreference.com/w/c/error/errno |
| 错误码列表 errno | https://man7.org/linux/man-pages/man3/errno.3.html |
| C 标准库手册 | https://en.cppreference.com/w/c |
| setjmp/longjmp | https://en.cppreference.com/w/c/program/setjmp |
| signal() 函数文档 | https://man7.org/linux/man-pages/man2/signal.2.html |
✅ 十、总结表
| 错误处理方式 | 适用场景 | 是否推荐 |
|---|---|---|
| 返回值检查 | ✅ 所有函数调用 | ✅ 强烈推荐 |
| errno + perror/strerror | ✅ 库函数出错 | ✅ 推荐 |
| setjmp/longjmp | 🚫 非结构化跳转 | ❌ 不推荐 |
| signal | ✅ 系统级错误,如 SIGSEGV | ⚠️ 有限度使用 |