最近,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(文件); 返回空值; } } 返回数组; }
确实,相当费力,而且有很多重复的代码……
但是请注意,上面的代码中有两种情况
这有什么意义?嗯,在实践中,我从未见过必须先恢复第一个执行的操作,然后恢复第二个,依此类推的情况。这意味着,当执行操作 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。)
以上就是C 中使用 goto 进行错误处理的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号