3

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

例如,在一些 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 必须是一个表达式,因此您不能真正从函数中转到或返回。有人会觉得这很有价值还是太有限了?

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

4

6 回答 6

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;

因为这种方法不会排除错误处理。

于 2009-04-13T12:40:50.590 回答
4

如果您唯一担心的是在一条线上塞进太多东西,为什么不直接使用:

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

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

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

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

于 2009-04-13T12:22:46.157 回答
1

这样的事情怎么样?

int result;

result = doSomething();

result = result && doSomething2();

result = result && doSomething3();

return result;

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

于 2009-04-13T13:25:56.923 回答
0

我建议您反对您提出的技术。首先,C 中的 exit 终止进程;它不只是返回。所以它仅限于少数致命错误的情况。

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

于 2009-04-13T12:25:34.183 回答
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;
}

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

于 2009-04-13T13:41:47.743 回答
0

我以前见过这个:

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

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

    return Result;
}

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

于 2009-04-13T14:04:05.617 回答