作为一种好的做法,您认为应该验证传递参数的函数中传递的参数,还是只确保函数始终接受正确的参数?
请考虑以下代码:
Matrix * add_matrices(const Matrix * left, const Matrix * right)
{
assert(left->rowsCount == right->rowsCount
&& left->colsCount == right->colsCount);
int rowsCount = left->rowsCount;
int colsCount = left->colsCount;
Matrix * matOut = create_matrix(rowsCount, colsCount);
int i = 0;
int j = 0;
for (i; i < rowsCount; ++i)
{
for (j; j < colsCount; ++j)
{
matOut->matrix[i][j] = left->matrix[i][j] + right->matrix[i][j];
}
}
return matOut;
}
你认为我应该在将参数传递给函数之前或之后检查参数,即。在功能?什么是更好的做法还是程序员依赖?
答案 0 :(得分:1)
内部。该功能可视为单个组件。 其作者最适合定义任何先决条件并进行检查。 在外面检查它们预先假定调用者知道可能不是这种情况的先决条件。
此外,通过将它们放在功能中,您可以确保每次通话都已被检查。
在离开功能之前,您还应该检查任何后置条件。
例如,如果您有一个名为assertValid(const Matrix*matrix)
的函数来检查对象的完整性(例如,数据不是NULL
指针),您可以在返回之前调用它。
始终如一地使用条件前和条件完整性是确保质量和定位故障的极为有效的方法。
答案 1 :(得分:1)
取决于函数是否为全局范围或本地static
。
全局函数无法控制调用它的内容。防御性编码将对收到的参数进行验证。但要做多少验证?
int my_abs(int x) {
assert(x >= -INT_MAX);
return abs(x);
}
上面的示例,在调试版本中,检查以确保绝对值函数将成功,因为abs(INT_MIN)
可能是个问题。现在,如果这个检查应该在生产版本中,则是另一个问题。
int some_string(char *s) {
assert(s != NULL);
...
}
在some_string()
中,可以删除对NULL-ness的测试,因为函数定义可能表明s
必须是一个字符串。尽管NULL
不是C字符串,但测试NULL-ness只是可以传递的许多不指向字符串的坏指针中的一个。所以这个测试的验证有限。
使用static
函数,代码处于本地控制之下。函数,调用者,两者都可以进行参数验证。该选择取决于代码。
存在用户/文件输入的反例。应立即进行基本数据鉴定。
int GetDriversAge(FILE *inf) {
int age;
if (fscanf("%d", &age) != 1) Handle_Error();
if (age < 16 || age > 122) Handle_Error();
return age
}
在OP的示例中,参数检查由函数完成,而不是调用者。如果没有等效性测试,该功能很容易以神秘的方式失败。这项检查的成本只是代码工作的一小部分。这使得它成为良好的检查,因为昂贵的检查(时间,复杂性)可能导致比他们解决的更多麻烦。请注意,如果调用代码执行了此测试并且从N个位置调用了add_matrices()
,那么该检查代码将以各种可能不一致的方式复制N次。
Matrix * add_matrices(const Matrix * left, const Matrix * right) {
assert(left->rowsCount == right->rowsCount
&& left->colsCount == right->colsCount);
结论:虽然存在异常,但更有说服力的理由来检查函数中的参数而不是调用者。
答案 2 :(得分:0)
我所做的是检查函数中的 参数并做出相应的操作(抛出异常,返回错误消息等)。我想这是函数的工作,检查传递的参数是否具有正确的数据类型并包含有效值。
答案 3 :(得分:0)
该函数应正确执行其任务,否则应抛出异常。客户端/消费代码可能会也可能不会进行检查,这取决于数据源以及您对它的信任程度,无论哪种方式,您还应该将函数调用包含在catch-try块中以捕获无效的参数异常。
修改强> 对不起,我把C搞糊涂了。您可以返回null,而不是抛出异常。客户端不一定要在调用之前检查数据(取决于数据源和其他因素,如性能约束),但必须始终检查null作为返回值。