如何处理c语言中的错误返回

如何处理c语言中的错误返回,c,C,当函数调用深入时,如何处理C中例程的错误返回 由于C不提供异常抛出机制,我们必须检查每个函数的返回值。例如,“a”例程可能由“b”调用,“b”可能由许多其他例程调用,因此如果“a”返回错误,我们必须在“b”中检查它,并在所有其他调用“b”的例程中检查它 如果“a”是一个非常基本的例程,那么它会使代码变得复杂。这样的问题有什么解决办法吗? 实际上,在这里,如果发生此类错误,我想得到一个快速返回路径,因此我们只需要在一个地方处理此错误。您制作一个错误代码列表(我使用enum)并在所有应用程序中使用“

当函数调用深入时,如何处理C中例程的错误返回

由于C不提供异常抛出机制,我们必须检查每个函数的返回值。例如,“a”例程可能由“b”调用,“b”可能由许多其他例程调用,因此如果“a”返回错误,我们必须在“b”中检查它,并在所有其他调用“b”的例程中检查它

如果“a”是一个非常基本的例程,那么它会使代码变得复杂。这样的问题有什么解决办法吗?
实际上,在这里,如果发生此类错误,我想得到一个快速返回路径,因此我们只需要在一个地方处理此错误。

您制作一个错误代码列表(我使用enum)并在所有应用程序中使用“平面”。 因此,如果b调用a并获取其中一个错误代码,您可以决定是继续,还是返回原始错误代码


用户/程序员应该有一个所有错误代码的列表…

AFAIK C是一种结构化编程语言

如果这是一个问题,同样适用于RTL函数,如fopen、fscanf等


所以我想传播错误更好。

恐怕就是这样。毫无例外,您必须检查调用链中每个函数的返回值。

有几种策略,但我发现最有用的是每个函数在成功时返回零,在错误时返回非零,其中特定值表示特定错误

这与早期返回逻辑相结合,实际上使函数非常易于阅读:

int
func (int param)
{
    int rc;
    rc = func2 (param);
    if (rc)
        return rc;

    rc = func3 (param);
    if (rc)
        return rc;

    // do something else
    return 0;
}

你可以使用宏

#define FAIL_FUNC( funcname, ... )    if ( !funcname( _VA_ARGS_ )  ) \
                                           return false;
通过这种方式,您可以维护相同的系统,但不必每次都编写相同的代码……

您可以使用setjmp()和longjmp()在C中模拟异常


在一般情况下,不会。您需要确保函数调用按预期工作。返回代码是确保这一点的主要机制(尽管设置全局错误号或错误标志也可能是适当的,这取决于上下文,但并不是说它简化了很多事情)

采用其他人建议的其中一种技术应该可以使错误检查变得统一且易于阅读。这将大大有助于保持事物的可维护性

然而,对于一些基本功能来说,失败的几率可能很低,不必担心

int sum(int a, int b) {
  return a + b;
}

真的不需要检查。但创建新窗口的系统调用确实应该是。最好的方法是尽可能以不会失败的方式设计函数。如果他们做I/O或内存分配或其他有副作用的事情,这是不可能的,所以要避免这些。例如,与其让函数分配内存并复制字符串,不如让函数获取预先分配的内存并将字符串复制到其中。或者,您可能只有一个发生I/O的地方,程序的其余部分只处理内存中的数据

或者,您可以决定某些类型的错误需要终止进程。例如,如果您的内存不足,很难从中恢复,因此您可能会崩溃。(但要做到这一点的方式是用户友好的:将相关数据连续地检查到磁盘上,以便用户可以恢复。)这样,函数就可以假装它们从未失败过


setjmp建议Murali VP也值得一看。

如果您使用了一个丑陋的
金字塔,如:

if (getting resource 1 succeeds) {
    if (getting resource 2 succeeds) {
        if (getting resource 3 succeeds) {
            do something;
            return success;
        }
        free resource 2;
    }
    free resource 1;
}
return failure;
或者与
goto
(看起来更好)等效:


他仍然需要检查大部分/所有函数调用,以确保一切顺利进行。他听起来好像想办法解决这个问题。但是,如果没有编写某种聪明的黑客(例如setjmp()或试图手动解开堆栈),他就完蛋了。在所有源代码中使用“平坦”错误代码是维护代码的好方法。@Adam-这对于普通用户来说太高级了,无法用作错误处理方法。基本上-是的-您需要使用一个简单的错误代码列表来检查所有内容-您有时可以将错误向上移动,而无需检查(如果res!=S_OK…)注意:整数溢出错误存在,因为像
sum()
中那样的简单代码没有进行错误检查。它们可以像缓冲区溢出错误一样被利用。这就是说,如果你编写的函数没有故障模式是最好的,但是你不能在很多时候避免它。一个有效的观点是,如果你足够努力,你可以破坏任何东西。但在某一点上,您必须就健壮性与可维护性做出决定。也就是说,在这种情况下,错误检查可能嵌入到函数中。在这种情况下,你只需要决定什么是合适的返回值(MAX_INT?)@Adam,我知道这只是一个例子,但是一个加两个数的函数是无用的,除非它使用了一个强大的错误检查工具,因为语言已经包含了一个运算符来求两个加数的和。大多数基本函数都会遇到同样的问题。所以我们只剩下有趣的函数来设计/编码。在这种情况下,将返回代码塞入实际返回值是一种比必要的更复杂的方法,因为它表明了变量的用途。非常简单,可以将指针作为参数传递,该参数将保存计算值或错误数据。不正确,简单添加两个整数的函数也可用作解释点的简单示例:P.它还可用于将其地址传递给需要指向算术运算的函数指针的函数…-)在您需要释放分配的内存或打开的文件描述符,或者。。。那么早期返回是一个资源泄漏。请注意setjmp()/longjmp()是非常原始的异常处理机制,结构不是很好。它们很有用,但与
try{…}catch(){…}
相比,它们有点凌乱。
if (getting resource 1 failed) goto err1;
if (getting resource 2 failed) goto err2;
if (getting resource 3 failed) goto err3;
do something;
return success;

err3:
free resource 2;
err2:
free resource 1;
err1:
return failure;