在编写函数库时,如果已经在父库中检查了函数参数是否应在子库中检查,是否存在共识?举例来说,请考虑以下代码片段:
uint8 er_remove(DE_LIST *deque)
{
ER_OBJECT *object;
uint8 result = ER_BAD_ARGUMENT;
if (deque != NULL)
{
result = de_remove_first(deque, (void **)&object);
}
return result;
}
假设函数de_remove_first()
也检查deque
参数以查看它是否为NULL,那么在er_remove()
中再次检查它是否被认为是好的形式?
从功能的角度来看,检查er_remove()
显然不是必需的。但是,对于读者来说,确实检查了deque
参数,并且它还消除了对未来代码修订中保留的de_remove_first()
中的检查的依赖。
有什么想法吗?
答案 0 :(得分:2)
这取决于库函数。
作为库外部接口的一部分记录的所有函数 - 用户调用的函数 - 应严格验证其参数(或者至少考虑这样做)。
库内部的函数,仅由库中的函数调用的函数,不需要对其参数进行额外的检查 - 除非存在只有该函数可以检查的条件。网关(接口)函数应该验证它们的参数,因此内部函数不需要重新验证它们。您可能仍然使用断言检查内部函数,因此如果函数错误地调用其中一个内部函数,则在开发期间发现这一点。但是那些断言应该永远很少发火。
答案 1 :(得分:0)
不,你没有。
er_remove
只是将该参数传递给另一个函数。使用deque
的逻辑完全在您的案例de_remove_first
内实现,是唯一有责任进行检查的人。
如果在de_remove_first
中再次执行此操作,则会使两个函数都绑定到该参数的值。这听起来像是一种矫枉过正。如果以后de_remove_first
被优化并且可以接受NULL指针,那么er_removed
会发生什么?
uint8 er_remove(DE_LIST *deque)
{
ER_OBJECT *object;
uint8 result = ER_BAD_ARGUMENT;
// Will you remember to remove this kind of checks?
if (deque != NULL)
{
// OK, I am now acceptable to a NULL "deque".
result = de_remove_first(deque, (void **)&object);
}
return result;
}
最好的情况是,它会提醒您这个额外的检查。您可以在调用路径中找到所有此类检查,并将其删除。最糟糕的是您忘记更新它,并且de_remove_first
中接受NULL参数的代码被绕过并且永远不会被执行。
另一个好处是,每次调用er_remove
或de_remove_first
时,调用者都不必检查此参数,从而简化了代码。如果free
是这样设计的,该怎么办?想一想。
// Check NULL whenever you call free to release a piece of memory
if (ptr != NULL)
{
free(ptr);
}