假设您正在为某种目的制作一个相当强大且经过严格测试的API ...就像数据结构一样。其他人将在他们自己的编码项目中下载和使用您的库,因此您希望它非常紧密。
在您的API中,您有许多这样的函数调用:
void remove(struct myDataStruct *s, void* element_to_be_removed)
{
if (element_to_be_removed == NULL)
// Output an error, not allowed to enter NULL.
else if (isEmpty(s))
// Output an error, cannot remove something from empty structure.
else
{
// Everything is safe.
// Begin removing the element to the data structure.
}
}
添加以下内容:
if (s == NULL)
// Output an error, data structure initialized by user is NULL.
如果我决定检查这一点,那么在将数据结构作为参数接受的每个API函数调用中,我都必须包含该检查。这是不好的做法吗?完全没必要?关于那种NULL检查的一般共识是什么?如果我没有检查NULL
那么有人可以这样做:remove(NULL, NULL)
我的图书馆会崩溃他的程序。
`
答案 0 :(得分:3)
我想这取决于API客户端是谁。在我工作的地方,通常我们只是在函数顶部使用assert(whatever!=NULL);
,并且有一个版本和一个调试版本。
但我想你可以做另一件事,比如不返回void
你可以返回包含输出代码的int
。例如,如果有错误则返回负数,如果成功则返回0
。有很多C API就是这样做的。
在我看来,如果您不需要超极简主义的应用程序,并且您不确定API调用者是否会使用正确的参数调用方法,您可以将其添加为额外的检查。否则,如果API客户端没有完全检查传递的参数是否正常,则需要长时间的调试。
答案 1 :(得分:3)
正如评论中所述,这个问题有很多方法。我将解释一个广泛使用的,以及它的好处。
怎么做,例如/伪代码
void remove(struct myDataStruct *s, void* element_to_be_removed)
{
// pre-conditions:
assert(s != NULL);
assert(element_to_be_removed != NULL);
assert(!isEmpty(s));
// implementation:
[...]
// optional post-conditions
// assert(s != NULL); <- maybe something more sensible
}
现在,这种方法的优点是什么:
assert()
都将消失。在调试版本中,他们将检查方法的合同是否遵循,否则立即停止,从而帮助您的图书馆用户找到他出错的地方。assert()
s。当然,这仅适用于通过例如参数NULL
被视为调用代码中的错误。大多数情况下,情况确实如此。甚至不会尝试发出有意义的错误并以某种方式恢复,而是快速失败。错误是一个错误,继续执行程序没有意义,这只会使调试变得复杂。
如果传递NULL
是意料之外的,但实际上并不是一个错误,那么你应该去&#34;记录&#34;改为接近。在您的示例中不是这种情况。
答案 2 :(得分:2)
我的回答非常基于意见:
当您致电某项功能时,必须验证是否有错误。您此时检查数据。例如:
int i;
int ret = scanf("%d", &i);
if (ret != 1) {
return ERROR;
}
// here `i` is valid
如果您多次检查数据的有效性,这是没用的。为此,API不应检查指针是否为NULL,因为用户应该在需要时完成它。
int main(void) {
int *api_data = malloc(sizeof *api_data);
if (api_data == NULL) {
return 1;
}
int ret = init_api_data(api_data); // api_data is valid
if (ret == ERROR) {
free(api_data);
return 1;
}
free(api_data);
}
int init_api_data(int *api_data) {
if (api_data == NULL) { // This is useless api_data has already been checked
return ERROR;
}
return OK;
}
这是C的理念,信任用户。当然,这会导致错误,这就是为什么我们试图创造一种更好的语言(例如,锈,哈斯克尔)。