C或C ++返回状态

C or C++ Return Status

编写返回一个表示状态代码的int的C或C++函数的最佳实践是什么?

具体来说,我想知道客户机的用法,但欢迎使用其他提示。

例如,我可以这样写吗:

1
2
3
int foo() {
  return 0;  // because everything was cool
}

然后像这样使用?

1
2
3
4
5
if (foo()) {
  // what to do if false, e.g. non-zero, e.g. not OK
} else {
  // what to do if true, e.g. zero, e.g. OK
}

这应该是可行的,因为最佳实践通常规定,状态代码0表示一切正常,而0表示布尔语句中的false

但是,这不太好,对吧:

1
2
3
4
5
if (!foo()) {
  // what to do if true
} else {
  // what to do if false
}


我们在C中使用这个,在我工作的地方:

1
2
3
4
int err = foo();
if (err) {
    // armageddon
}

赋值和if可以组合,但是如果函数调用更复杂,它会变得更混乱,有些人会被条件中的赋值所迷惑(gcc讨厌它)。

对于C++,如果有可用的话,我更喜欢例外。

编辑:我建议成功时返回0,错误时返回任何其他值。这就是Unix命令行实用程序所做的。


如果您真的想用这种方式使用状态代码,请将它们与描述状态代码意图的enum#define语句块一起使用。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum
{
   kSuccess = 0,
   kFailure = -1,
}

function foo()
{
    return kSuccess;
}

if (kSuccess == foo())
{
    // Handle successful call to foo
}
else
{
    // Handle failed call to foo
}

这样,目的是明确的,当有人想要在将来使用或维护您的代码时,不存在容易出错的猜测。


1
2
3
4
5
if (foo()) {
  // what to do if false
} else {
  // what to do if true
}

这种方法的问题是过度嵌套。假设您有三个要调用的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
if(foo1()) {
    if(foo2()) {
        if(foo3()) {
            // the rest of your code
        } else {
            // handle error
        }
    } else {
        // handle error
    }
} else {
    // handle error
}

要解决过量嵌套问题,请反转返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(!foo1()) {
    // handle error
    return;
}

if(!foo2()) {
    // handle error
    return;
}

if(!foo3()) {
    // handle error
    return;
}

这个解决方案遇到了另一个问题。它混合了程序逻辑和错误处理代码。这使一切变得复杂。理想情况下,您希望程序逻辑和错误处理分开。这个问题可以用goto解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if(!foo1())
    goto error1;

if(!foo2())
    goto error2;

if(!foo3())
    goto error3;

return;

error1:
    // handle error
    return;
error2:
    // handle error
    return;
error3:
    // handle error
    return;

干净多了。

此外,goto还可以解决资源分配问题。有关更多详细信息,请参阅EliBendersky在C中使用Goto进行错误处理。


最好的做法是记录您的代码,以便您和其他人在执行错误检查时可以快速查找返回代码。


返回状态应该在接口中定义,并为调用方所知。一些失败时返回0(因为很容易检查!),一些成功时返回0(因为它们有enum的错误代码,OK是第一项)。

没有法律或标准,每个接口定义自己的约定。在C++中使用异常。


只需跳上另一个适合您情况的选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum fooret { GOOD, BAD, UGLY, WORSE };

fooret foo();  // defined elsewhere

switch(foo())
{
case BAD:
case UGLY:
   // maybe a recoverable failure(s)...
   // take appropriate actions
   break;
case WORSE:
   // maybe non-recoverable
   break;
case GOOD:
   // successful, take appropriate actions
   break;
}


1
2
3
4
5
6
7
8
9
10
int foo() {
   try{
    ...
   return 1
   }
   catch
   {
   return 0;  // because everything was cool
   }
}

我首先将所有内容包装在一个try/catch块中。另外,它可能会使更多的场景返回布尔值,而不是使用和int。在if语句中进行测试时,这只是稍微直观一点。