返回介绍

4. 错误

发布于 2024-10-12 21:58:09 字数 1966 浏览 0 评论 0 收藏 0

错误处理(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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文