作为一种好的做法,您认为应该在参数传递到的函数中验证传递的参数,还是简单地确保该函数始终接受正确的参数?
考虑以下代码:
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;
}
您认为我应该在将参数传递给函数之前还是之后检查参数,即。在函数中?什么是更好的做法还是依赖于程序员?
内部。 该函数可以被视为一个单独的组件。 它的作者最适合定义任何先决条件并检查它们。 在外部检查它们的前提是调用者知道先决条件,但情况可能并非如此。
此外,通过将它们放在函数中,您可以确保每次调用都会被检查。
您还应该在离开该函数之前检查任何后置条件。
例如,如果您有一个名为
int assertValid(const Matrix*matrix)
的函数来检查对象的完整性(例如,数据不是 NULL
指针),您可以在进入所有函数时以及从修改 Matrix
的函数返回之前调用它.
一致使用前后条件完整性是确保质量和定位故障的极其有效的方法。
在实践中,过分遵守这条规则通常会导致不可接受的表现。
assert()
宏或类似的条件编译结构是一个很好的资产。请参阅<assert.h>
。
取决于函数的范围是全局的还是局部的
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 的测试可能会被删除,因为函数定义可能会声明 s
must 是一个字符串。 尽管 NULL
不是 C 字符串,但测试 NULL 性只是可以传递的许多不指向字符串的错误指针之一。 所以这个测试的验证有限。
具有
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);
该函数应该正确执行其任务,否则,它应该抛出异常。客户端/使用代码可能会或可能不会进行检查,这取决于数据源以及您对它的信任程度,无论哪种方式,您还应该将函数调用包含在 catch-try 块中以捕获无效参数异常。
抱歉,我把 C 和 C++ 搞混了。您可以返回 null,而不是抛出异常。客户端不一定要在调用之前检查数据(取决于数据源和性能约束等其他因素),但必须始终检查 null 作为返回值。