我注意到标准 c 库包含几个不检查输入参数(是否为 NULL)的字符串函数,例如 strcmp:
int strcmp(const char *s1, const char *s2)
{
for ( ; *s1 == *s2; s1++, s2++)
if (*s1 == '\0')
return 0;
return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1 : +1);
}
许多其他人不做同样的验证。这是一个好习惯吗?
在其他图书馆,我看到他们检查每一个参数,像这样:
int create_something(int interval, int mode, func_t cb, void *arg, int id)
{
if (interval == 0) return err_code_1;
if (valid(mode)) return err_code_2;
if (cb == NULL) return err_code_3;
if (arg == NULL) return err_code_4;
if (id == 0) return err_code_5;
// ...
}
哪个更好?当您设计一个 API 时,您会检查所有参数以使其正常运行还是让它崩溃?
最佳答案
我想争辩说,在期望有效指针的库函数中不检查指针是否为 NULL
实际上比返回错误或默默地忽略它们更好。
NULL
不是唯一无效的指针。还有数十亿个其他指针值实际上是不正确的,为什么我们要优先处理一个值?
错误返回经常被忽略、误解或管理不善。忘记检查一个错误返回可能会导致程序运行异常。我想争辩说,一个默默地行为不端的程序比一个根本不工作的程序更糟糕。不正确的结果可能比没有结果更糟糕。
早期和困难的失败简化了调试。这是最大的原因。程序的最终用户不希望程序崩溃,但作为程序员,我是库的最终用户,我实际上希望它崩溃。崩溃表明我需要修复一个错误,我们越快找到错误并且崩溃越接近错误的源头,我就能更快更容易地找到它并修复它。 NULL
指针取消引用是需要捕获、调试和修复的最微不足道的错误之一。这比翻遍千兆字节的日志来发现一行“create_something had a null pointer”要容易得多。
对于错误返回,如果调用者捕获到该错误,返回错误本身(在您的示例中为 err_create_something_failed
)并且其调用者返回另一个错误(err_caller_of_create_something_failed
)?然后你有一个错误 return 3 functions away,这甚至可能不表明实际出了什么问题。即使它设法指出实际出了什么问题(通过一个完整的错误处理框架来准确记录整个调用者链中错误发生的位置),您唯一能做的就是在中查找错误值一些表,并从中得出结论,create_something
中有一个 NULL
指针。相反,您本可以打开调试器并准确查看违反假设的位置以及导致该问题的确切函数调用链,这会很痛苦。
本着同样的精神,您可以使用 assert
来验证其他函数参数,从而尽早导致易于调试的故障。断言崩溃,您拥有导致问题的完整正确的调用链。我只是不会使用断言来检查指针,因为它毫无意义(至少在具有内存管理的操作系统上)并且在给你相同的行为(减去打印的消息)的同时让事情变慢。
关于c - 我是否应该检查函数的每个参数以确保函数运行良好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39342826/