我在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*
指针真的没有明智的理由,如果他们不知道他们那么多,我应该让他们知道。
答案 0 :(得分:1)
析构函数(在抽象意义上,而不是C ++意义上)确实应该永远不会失败,无论如何。与此一致,如果传递空指针,free
被指定返回而不做任何事情。因此,我认为List_destroy
做同样的事情是合理的。
但是,提示崩溃也是合理的,因为通常期望C库函数在传递无效指针时崩溃。如果采用此选项,则应该通过继续并取消引用指针并让内核触发assert
的而不是来崩溃,因为assert
具有不同的崩溃签名
绝对不要更改函数签名,以便它可能返回失败代码。这是close()
的作者所犯的错误,我们在40年后仍在支付这些错误。
答案 1 :(得分:1)
通常,如果违反了某个函数的约束,则有几个选项:
assert
或致电abort
或exit
等)哪里(但这是我的个人意见)这是一个很好的经验法则:
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错误并让程序员修复它来为你处理它。