0

0

C 中使用 goto 进行错误处理

PHPz

PHPz

发布时间:2024-07-10 11:25:46

|

591人浏览过

|

来源于dev.to

转载

c 中使用 goto 进行错误处理

最近,Python Brasil 邮件列表上开始了关于使用异常的原因的讨论。有一次,一位非常有能力的参与者评论了通过函数返回来处理错误是多么困难,就像在 C 中一样。

当你有一个复杂的算法时,每个可能失败的操作都需要一系列的 if 来检查操作是否成功。如果操作失败,您需要恢复之前的所有操作以退出算法,而不改变程序的状态。

让我们看一个例子。假设我有以下结构来表示数组:

typedef 结构体 {
    整数大小;
    整数*数组;
} array_t;

现在,我将编写一个函数,从文本文件中读取要放置在这些数组之一中的元素数量,然后读取元素本身。该函数还将分配数组结构和数组本身。问题是这个函数很容易出错,因为我们可能会失败:

  • 打开给定的文件;
  • 分配结构体;
  • 由于输入/输出错误或文件结尾而读取给定文件中的元素数量;
  • 分配内存来存储要读取的元素;
  • 由于输入/输出错误或文件结尾而读取其中一个元素。

很复杂吧?请注意,如果我们设法打开文件但无法分配结构,则必须关闭该文件;如果我们设法打开文件并分配结构体,但无法从文件中读取元素数量,则必须释放结构体并关闭文件;等等。因此,如果我们检查所有错误并采用在出现错误时返回 NULL 的传统,我们的函数将如下所示:

array_t *readarray(const char *文件名) {
    文件*文件;
    array_t *数组;
    整数我;

    文件 = fopen(文件名, "r");
    如果(文件== NULL)返回NULL;

    数组= malloc(sizeof(array_t));
    如果(数组== NULL){
        fclose(文件);
        返回空值;
    }

    if (fscanf(文件, "%d", &(数组->大小)) == EOF) {
        自由(数组);
        fclose(文件);
        返回空值;
    }

    数组->数组 = malloc(sizeof(int) * 数组->大小);
    if (数组->数组 == NULL) {
        自由(数组);
        fclose(文件);
        返回空值;
    }

    for (i = 0; i < 数组->大小; i++) {
        if (fscanf(文件, "%d", 数组->数组 + i) == EOF) {
            自由(数组->数组);
            自由(数组);
            fclose(文件);
            返回空值;
        }
    }
    返回数组;
}

确实,相当费力,而且有很多重复的代码……

但是请注意,上面的代码中有两种情况

吐槽大师
吐槽大师

吐槽大师(Roast Master) - 终极 AI 吐槽生成器,适用于 Instagram,Facebook,Twitter,Threads 和 Linkedin

下载
  1. 其中,当我有两个操作要恢复时,我需要先恢复最后执行的一个,然后再恢复前一个。例如,当释放结构体和整数数组时,我需要先释放整数数组,然后释放结构体。如果我先释放结构体,以后可能无法释放数组。
  2. 在其他情况下,顺序并不重要。例如,如果我要释放结构体并关闭文件,那么执行此操作的顺序并不重要。这意味着我也可以先恢复最后执行的操作,然后恢复第一个操作。

这有什么意义?嗯,在实践中,我从未见过必须先恢复第一个执行的操作,然后恢复第二个,依此类推的情况。这意味着,当执行操作 a()、b()、c() 等时,恢复它们的“自然”方法是以相反的顺序调用恢复函数,如下所示:

a();
b();
C();
/* ... */
revert_c();
恢复b();
恢复a();

现在窍门来了。在上面的代码中,在每个操作之后,我们都会放置一个 if 来检查它是否失败。如果失败,将执行 goto 到上次成功操作的恢复函数:

a();
if (failed_a()) 转到 FAILED_A;
b();
if (failed_b()) 转到 FAILED_B;
C();
if (failed_c()) 转到 FAILED_C;
/* ... */
revert_c();
失败_C:
恢复b();
失败_B:
恢复a();
失败_A:
返回;

如果a()失败,算法返回;如果 b() 失败,算法将转到 FAILED_B:,恢复 a() 并返回;如果 c() 失败,算法将转到 FAILED_C,恢复 b(),恢复 a(),然后返回。你能看到图案吗?

如果我们将此模式应用于 readarray() 函数,结果将类似于:

array_t *readarray(const char *文件名) {
    文件*文件;
    array_t *数组;
    整数我;

    文件 = fopen(文件名, "r");
    如果(文件== NULL)转到FILE_ERROR;

    数组= malloc(sizeof(array_t));
    如果(数组== NULL)转到ARRAY_ALLOC_ERROR;

    if (fscanf(文件, "%d", &(数组->大小)) == EOF)
        转到SIZE_READ_ERROR;

    数组->数组 = malloc(sizeof(int) * 数组->大小);
    if (array->array == NULL) 转到 ARRAY_ARRAY_ALLOC_ERROR;

    for (i = 0; i < 数组->大小; i++) {
        if (fscanf(文件, "%d", 数组->数组 + i) == EOF)
            转到 ARRAY_CONTENT_READ_ERROR;
    }
    返回数组;

    ARRAY_CONTENT_READ_ERROR:
    自由(数组->数组);
    ARRAY_ARRAY_ALLOC_ERROR:
    大小读取错误:
    自由(数组);
    ARRAY_ALLOC_ERROR:
    fclose(文件);
    文件错误:
    返回空值;
}

这种模式有什么优点?嗯,它减少了操作反转代码的重复,并将错误处理代码与功能逻辑分离。事实上,虽然我认为异常是最好的现代错误处理方法,但对于本地错误处理(在函数本身内部),我发现这种方法更实用。

(这篇文章是 Tratamento de error em C com goto 的翻译,最初发表于 Suspensão de Descrença。)

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

254

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1089

2024.03.01

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

847

2023.08.22

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

492

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

382

2023.10.25

go语言goto的用法
go语言goto的用法

本专题整合了go语言goto的用法,阅读专题下面的文章了解更多详细内容。

138

2025.09.05

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

490

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

202

2025.07.04

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号