一 概述
二 类型
三 语句
四 函数
五 数据
六 内存
七 代码
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
4. 错误
错误处理(error handling)分为检测(detection)和恢复(recovery)两个步骤,也就是发现已发生的错误并确定如何处理。处理不仅仅是输出错误信息,还要关闭和清理某些资源。比如:动态内存、已打开文件,以及线程锁等等。
对库函数而言,不应该有任何导致进程终止操作。将错误反馈给调用方,由它决定处理策略。
全局错误指示器
在全局存储函数调用的错误状态,比如标准库使用 errno 宏。
此方法的问题在于:
- 调用方可能会忽略该状态,导致不可预测行为发生。
- 如不立即检查,会被后续调用修改,导致状态丢失。
- 必须和函数返回的错误代码配合使用。
C11: errno, thread-local, thread-safe。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main (void) { FILE *f = fopen("non_existent", "r"); if (f == NULL) { printf("%d, %s\n", errno, strerror(errno)); perror("fopen() failed"); return -1; } fclose(f); return 0; }
2, No such file or directory fopen() failed: No such file or directory
内置错误指示器
对象自持错误状态,避免全局指示器的弊端。比如 FILE 类型,针对读写错误设置错误状态。
FILE
ferror
,feof
,clearerr
函数返回值
返回不同状态码(enum、int、null、eof)代表不同错误。缺点是检查错误返回值会大大增加代码量,同样容易被调用方忽略。
getc
: int or EOF。malloc
: pointer or NULL。
Go(value, error)
和 Rust() -> Result<T, E>
算是对此方式的改进,避免了返回值仅用于错误状态的尴尬。可以考虑借鉴!
- 使用枚举而非数字表示错误状态。
- 提供将错误码转换为可阅读字符串的函数。
- 提供 callback 参数注入错误处理?(感觉不好)
其他方案
除上述三种,还有:
- 用 signal 实现,在信号处理程序里作出决策。
- 用 longjmp 实现跨函数错误处理。
- 用 goto chain 集中错误处理代码。
- 用 callback 参数注入错误处理。
int do_something (void) { f1 = fopen("a", "w"); if (f1 == NULL) goto ERR_F1; f2 = fopen("b", "w"); if (f2 == NULL) goto ERR_F2; m = malloc(10); if (m == NULL) goto ERR_MEM; free(obj); return 0; ERR_MEM: // 错误处理,注意次序。 fclose(f2); ERR_F2: fclose(f1); ERR_F1: return -1; }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论