C中的短路错误处理

时间:2009-04-13 12:17:22

标签: c error-handling short-circuiting

我想知道是否有更好的方法来处理C中的情况,只要在一系列表达式中遇到错误就要退出函数。 (在这种情况下,它是一个在出错时返回NULL的函数)

e.g。在某些C代码中,他们试图通过将一系列语句与AND(&&)相结合来缩短错误处理。

return doSomething() && 
     doSomething2() && 
     doSomething3() && ... ;

这让我感到恼火,因为我们在一行声明中将塞进一行。但我想替代方案是

if (!(value = doSomething()))
    return NULL;
if (!(value = doSomething2()))
    return NULL;
etc
return value;

但是我在perl和bash脚本中看到的短路错误评估呢。

int die(int retvalue) {
    exit(retvalue);
}
.....
(value = doSomething()) || die(1);
(value = doSomething2()) || die(2);
(value = doSomething3()) || die(3);
return value;

这个问题的主要问题是RHS必须是一个表达式,所以你不能真正转到函数或从函数返回。有人会发现这个有价值的还是太有限了?

编辑:我想我应该在第一个例子中包含换行符。问题是如果你决定在中间添加另一个表达式,你需要小心。

6 个答案:

答案 0 :(得分:5)

我已经看到在C中使用GOTO就是出于这个目的。

因为在C中没有'finally'结构,所以即使你提前退出函数,也需要一种释放所有内存的方法。

所以它基本上如下 (如果我错了,有人可以纠正我的C语法,我有点生疏了)

int foo()
{
    /* Do Stuff */
    if(!DoSomething())
        GOTO Failure;

    if(!DoSomething2())
        GOTO Failure;

    if(!DoSomething3())
        GOTO Failure;

    /* return success */
    return true; 

    Failure:
    /* release allocated resources */
    /* print to log if necessary */
    return false;
}

重要提示 不要将GOTO用于执行流程。它们只应在出错时使用,并且只能转到当前函数的末尾。如果你将它们用于其他任何事情,你就会创造出可能破坏现实结构的意大利面条代码。只是不要这样做。

修改

正如其中一张海报所指出的那样,使用Exit(x)将会终止整个程序,从而保留该解决方案以防止致命错误。然而,您在一条线上的原始提出的解决方案(DS()& DS2()& DS3())对于错误处理提出了问题。

如果要将函数包装在某种特定于函数的错误处理中,则在将函数调用全部包装在一行中时无法执行此操作。所以,至少你可以做一些像

这样的事情
int result1 = 0;
int result2 = 0;
int result3 = 0;

result1 = DoSomething();

if(result1)
    result2 = DoSomething2();

if(result2)
    result3 = DoSomething3();

return result1 && result2 && result3;

因为这种方法不会妨碍错误处理。

答案 1 :(得分:4)

如果您唯一的担忧是在一条线上塞满太多,那么为什么不使用:

return doSomething()
    && doSomething2()
    && doSomething3()
    && ... ;

我倾向于将第二种情况写成:

if (!(value = doSomething()))  return NULL;
if (!(value = doSomething2())) return NULL;
: : : : :
return value;

(甚至排列返回以使其可读)因为我喜欢在屏幕上看到尽可能多的代码(并且您可以根据需要在行之间插入其他检查)。

答案 2 :(得分:1)

这样的事情怎么样?

int result;

result = doSomething();

result = result && doSomething2();

result = result && doSomething3();

return result;

这使用与第一个示例类似的方式进行短路,但它允许您将其分成多行并添加注释等。

答案 3 :(得分:0)

我建议不要使用您提出的技术。首先,C中的退出终止了该过程;它不仅仅是回报。所以它仅限于少数致命错误。

在我看来,你试图避免的第一个解决方案是最好和最容易理解的。

答案 4 :(得分:0)

根据我的经验,用C语言写这个的惯用方法是用一系列if语句来启动函数,该语句检查函数其余部分的前置条件。如果不满足前提条件,则立即返回错误代码。这样,当你到达函数体的主要部分时,你知道一切都很好,并且可以使代码尽可能简单。

换句话说,你是第二种方法。

作为一个例子,可以编写一个函数来复制字符串,如下所示:

int copy_string(char *source, char *target, size_t max)
{
    if (source == NULL)
        return -1;
    if (target == NULL)
        return -1;
    source_len = strlen(source);
    if (source_len + 1 > max)   // +1 for NUL
        return -1;
    memcpy(target, source, source_len + 1);
    return 0;
}

(我喜欢为错误返回-1的Unix系统调用约定,但这是一个有争议的问题,与这个问题无关。)

答案 5 :(得分:0)

我之前见过这个:

int Function (void)
{
    int Result = DoSomething();

    if (Result) Result = DoSomething2();
    if (Result) Result = DoSomething3();
    if (Result) Result = DoSeomthing4();

    return Result;
}

它看起来很整洁,易于对齐。它不会立即退出,但它不会执行任何其他操作。这当然假设函数在成功时返回非零值。否则,如果函数在失败时返回非零,则可以简单地使用if (!Result)