如何处理void函数中的错误条件

时间:2014-01-06 05:12:50

标签: c error-handling

我在C中创建了一个数据结构和算法库用于学习目的(所以这不一定必须是防弹),我想知道void函数应该如何处理前置条件的错误。如果我有一个破坏列表的功能,如下所示:

void List_destroy(List* list) {

    /*
      ...
      free()'ing pointers in the list. Nothing to return.
      ...
    */
}

其中一个前提条件是list != NULL,否则该函数会在调用者的脸上因段错而爆炸。

据我所知,我有几个选择:一,我抛出一个assert()语句来检查前置条件,但这意味着该函数仍然爆炸呼叫者的脸(据我所知,这对图书馆来说是一个很大的禁忌),但至少我可以提供一个错误信息;或者两个,我检查前提条件,如果它失败了,我跳转到一个错误块并且只是return;,然后默默地继续,但是然后调用者不知道List*是NULL。

这些选项似乎都没有特别吸引人。此外,为简单的destroy()函数实现返回值似乎应该是不必要的。

编辑:谢谢大家。我决定实现(在所有我的基本列表函数中,实际上)传递给函数的NULL List*指针的一致行为。所有函数都跳转到错误块exit(1),并沿着“无法销毁NULL列表”的行向stderr报告错误消息。 (或推,或弹出,或其他)。我的理由是,调用者无论如何都应该传递NULL List*指针真的没有明智的理由,如果他们不知道他们那么多,我应该让他们知道。

4 个答案:

答案 0 :(得分:1)

析构函数(在抽象意义上,而不是C ++意义上)确实应该永远不会失败,无论如何。与此一致,如果传递空指针,free被指定返回而不做任何事情。因此,我认为List_destroy做同样的事情是合理的。

但是,提示崩溃是合理的,因为通常期望C库函数在传递无效指针时崩溃。如果采用此选项,则应该通过继续并取消引用指针并让内核触发assert而不是来崩溃,因为assert具有不同的崩溃签名

绝对不要更改函数签名,以便它可能返回失败代码。这是close()的作者所犯的错误,我们在40年后仍在支付这些错误。

答案 1 :(得分:1)

通常,如果违反了某个函数的约束,则有几个选项:

  1. 什么也不做,成功
  2. 返回一些表示失败的值(或将参数指向的某些内容设置为某些错误代码)
  3. 随机崩溃(即引入未定义的行为)
  4. 可靠崩溃(即使用assert或致电abortexit等)
  5. 哪里(但这是我的个人意见)这是一个很好的经验法则:

    • 如果您认为不遵守约束条件(即它们不是真正的约束条件),则第一个选项是正确的选择,对此的一个很好的例子是free
    • 如果呼叫者无法事先知道呼叫是否成功,则第二个选项是正确的选择;一个很好的例子是fopen
    • 如果前两个选项不适用,第三个和第四个选项是一个不错的选择。一个很好的例子是memcpy。我更喜欢使用assert(第四个选项之一),因为它可以同时启用:如果有人不愿意阅读您的文档并为读取它的人引入未定义的行为,则可靠地崩溃(他们会通过遵守您的方式来防止这种情况发生约束),取决于它们是否使用NDEBUG进行编译。取消引用指针参数可以作为assert,因为如果这些人传递了无效的指针,它会使你的程序崩溃(这是正确的,不读文档的人应该尽快崩溃)。 LI>

    所以,在你的情况下,我会使它与free类似,并且在没有做任何事情的情况下会成功。

    HTH

答案 2 :(得分:0)

如果你不想从函数中返回任何值,那么最好再为errCode添加一个参数。

void List_destroy(List* list, int* ErrCode) {


*ErrCode = ...
}

编辑: 问题标记为C时,将&更改为*

答案 3 :(得分:0)

我想说只是在列表为NULL的情况下返回才有意义,这表明列表是空的(不是错误条件)。如果list是一个无效指针,你就无法检测到它,让内核通过给出一个seg错误并让程序员修复它来为你处理它。