检查返回int的函数时出错

时间:2011-01-23 20:35:05

标签: c error-handling

如果我有一个返回某种指针的函数,我会在错误时将返回值设置为NULL来检查错误。

char *foo(void) {
  //If stuff doesn't go okay
  return NULL;
}

char *bar = foo();
if(!bar) return 1;

这很有效,因为我知道在这种情况下我永远不会想要返回NULL。

但是,有时候我会有返回整数的函数(在这种情况下,我从配置文件中读取内容)。但是,问题是现在无法检查返回的值是否有错误,因为任何值(包括0)都可能是真的。

一些解决方法:

  1. 在函数
  2. 中包含错误代码参数
  3. 返回错误代码并将int指针包含为参数
  4. 这两个问题都是我有一组函数,它们都做同样的事情,但对于不同的类型,我想维护一个常规接口,以便它们可以以相同的方式使用。

    是否有其他解决方案不涉及更改功能的接口?处理这种情况最常见的方法是什么?

    CHOSEN SOLUTION

    感谢您对此的所有想法和答案。

    最后我决定,如果函数用于返回某些数据,则只能通过error参数返回错误。否则,将直接返回错误。

    我选择了这个根,因为通常我发现当返回更复杂的数据形式时,潜在错误的数量几乎总是大于1.这意味着使用NULL作为错误数据的唯一来源是不切实际的,因为它意味着没有办法确定错误究竟是什么。对于将数据作为int返回的函数,也无法区分多个不同的错误代码和有效数据。

    当然,对于没有真正返回任何数据的函数,情况也是如此。在这种情况下,我可以自由地将返回值用作错误代码。

    我也喜欢上述模式明确区分返回数据的函数和不返回数据的函数。

7 个答案:

答案 0 :(得分:4)

全局变量的可怕解决方案 - 类似于errno。不推荐。

您可以使用函数从上次调用集合中的某个函数中检索错误条件 - 不建议再次使用。这不是线程安全的,并且您可能会跨函数共享错误 - 而不是能够从您使用的函数中查询最后一个错误(尽管可以通过适当的设计来修复)。

您可能决定所有函数都返回一个可测试的状态,并通过指针参数返回该值。这使您可以在所有功能中保持一致性,并改变使用模式。你会一直写:

if ((rc = your_function(in1, in2, &out)) != 0)
    ...handle error...
else
    ...use returned value...

这可能是最不讨厌的解决方案。

将指针传递给错误结构或指向错误结构指针的另一个主要选择是容易导致资源管理问题。如果错误结构是固定大小,则可以使其工作,从而消除资源管理问题。

Error_t err;

int result = your_function(in1, in2, &err);
if (err.code != 0)
    ...handle error...
else
    ...use returned value...

您无法在调用函数的同一行中测试错误条件,有些人会认为这是一种麻烦。当你处于一系列操作的第三个'else if'子句时,它最重要 - 然后你必须引入一个新级别的嵌套,其中错误代码作为返回值不需要。有利的是,错误结构中的信息可以比单个错误号更全面,这可能会导致更好的诊断。

您可以使用混合解决方案 - 错误返回代码和错误参数,以包含用于改进诊断的额外信息。但是,很少选择AFAIK。

答案 1 :(得分:3)

最好也是最常见的方式(例如Windows API实践)是编写所有错误处理,如下所示:

int func(Error_t * error);

其中Error_t是某种错误指示。这样,它不会干扰返回的结果。

答案 2 :(得分:2)

你可以牺牲一个int的最大负数或正数,例如INT_MAX。只需创建一个const MY_INT_ERROR_VALUE并与之进行比较。

答案 3 :(得分:1)

有几种方法可以做你想要的,但是所有这些方法都会让你有一个非同构的界面(或者列出一种非同类的方法来测试错误)。

如果您无法更改函数的签名,则可以使用函数在发生错误时设置的全局变量,但我认为此解决方案比更改界面更脏。

另一个解决方案可能是使用跳转或系统信号模拟异常,但我仍然不建议这样做(这也不是可移植的)。

答案 4 :(得分:1)

这个怎么样:

void *foo(void *res)
{
    // cast the void *res to whatever type the result would be
    if (/* successfull */) 
        return res;

    /* error */
    return NULL;
}

通过这种方式,您将拥有所有这些功能的相同签名,并且您还可以轻松检查错误。

答案 5 :(得分:0)

我的偏好是,在使用缺少异常处理的语言编写代码时,总是将返回值用作错误指示符。

int get_value(int *value)
{
    if ( input_ok )
        *value = input;
        return 0;
    return -1;
}

这似乎不实用,因为它会强制您使用中间变量来存储函数结果,但事实证明它有很多次来捕获错误并正确处理它们(您永远不能依赖用户输入)。

另外,你应该注意,有一个特殊值来显示错误并不是一个好主意:你永远不知道你的代码会发生什么。如果以后要添加新功能,不幸的是特殊值对此功能有用,您会怎么做?用另一个特殊错误值写一个新函数?如果输入应覆盖整个返回类型范围怎么办?如果这是一个更大的项目的一部分,你怎么能确保每个可能使用你的功能的程序员都知道特殊的价值?

安全方面的错误:不要使用特殊值并使用专用的错误处理路径。

请注意,您可以撤消上述代码并撰写int get_value(int *error);

答案 6 :(得分:-1)

1。使用NaN,如浮动协处理器。选择一个可能从未用作结果的值来表示错误。

# define VALUE_OF_FAIL (-99999)

那么:

char foo(void) {   
  // If stuff doesn't go okay   
  return VALUE_OF_FAIL; 
}

char bar = foo(); 
if (bar == VALUE_OF_FAIL) return 1;

同样,你必须保证,常规计算的结果永远不会是VALUE_OF_FAIL。从整数范围的外围选择一个数字。您可以定义更多失败代码,但是您应该编写一个函数来检查变量是否失败。

2。将您的计算代码整理到一个类中,然后添加 isValid()方法:

class FooBar {

  public FooBar() {  // constructor
    this.fail = false;   // initialize here...
  }

  char foo(void) {  // the computation method

    this.fail = false;  // ...or here

    // If stuff doesn't go okay   
    this.fail = true;  // or error code
    return 0; // any value
  }

  bool failed() {
    return this.fail; 
  }

}

// outside the class    
fb = new FooBar();
char bar = fb->foo(); 
if (fb->failed()) return 1;