您应该在传递参数之前检查传入函数的参数,还是在函数中检查它们?

问题描述 投票:0回答:4

作为一种好的做法,您认为应该在参数传递到的函数中验证传递的参数,还是简单地确保该函数始终接受正确的参数?

考虑以下代码:

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;
}

您认为我应该在将参数传递给函数之前还是之后检查参数,即。在函数中?什么是更好的做法还是依赖于程序员?

c parameter-passing
4个回答
5
投票

内部。 该函数可以被视为一个单独的组件。 它的作者最适合定义任何先决条件并检查它们。 在外部检查它们的前提是调用者知道先决条件,但情况可能并非如此。

此外,通过将它们放在函数中,您可以确保每次调用都会被检查。

您还应该在离开该函数之前检查任何后置条件。

例如,如果您有一个名为

int assertValid(const Matrix*matrix)
的函数来检查对象的完整性(例如,数据不是
NULL
指针),您可以在进入所有函数时以及从修改
Matrix
的函数返回之前调用它.

一致使用前后条件完整性是确保质量和定位故障的极其有效的方法。

在实践中,过分遵守这条规则通常会导致不可接受的表现。

assert()
宏或类似的条件编译结构是一个很好的资产。请参阅
<assert.h>


1
投票

取决于函数的范围是全局的还是局部的

static

  1. 全局函数无法控制调用它的内容。 防御性编码将对收到的参数进行验证。 但要做多少验证呢?

    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 性只是可以传递的许多不指向字符串的错误指针之一。 所以这个测试的验证有限。

  1. 具有

    static
    功能,代码受本地控制。 参数验证可以由函数、调用者、两者或两者都不进行。 该选择取决于代码。

  2. 用户/文件输入存在反例。 基本数据鉴定应及时进行。

    int GetDriversAge(FILE *inf) {
      int age;
      if (fscanf("%d", &age) != 1) Handle_Error();
      if (age < 16 || age > 122) Handle_Error();
      return age
    }
    
  3. 在OP的示例中,参数检查是由函数而不是调用者完成的。 如果没有等价性测试,该函数很容易以神秘的方式失败。 这里的检查成本只是代码工作的一小部分。 这使得它成为一个“好的”检查,因为昂贵的检查(时间、复杂性)可能会导致比它们解决的问题更多的问题。请注意,如果调用代码执行了此测试,并且从 N 个位置调用了 add_matrices(),则该检查代码将以各种(可能是不一致的)方式复制 N 次。

    
    
    Matrix * add_matrices(const Matrix * left, const Matrix * right) { assert(left->rowsCount == right->rowsCount && left->colsCount == right->colsCount);

    
    
    
结论:尽管存在异常,但检查函数中的参数比检查调用者中的参数更有说服力。


0
投票

该函数应该正确执行其任务,否则,它应该抛出异常。客户端/使用代码可能会或可能不会进行检查,这取决于数据源以及您对它的信任程度,无论哪种方式,您还应该将函数调用包含在 catch-try 块中以捕获无效参数异常。


0
投票
编辑:

抱歉,我把 C 和 C++ 搞混了。您可以返回 null,而不是抛出异常。客户端不一定要在调用之前检查数据(取决于数据源和性能约束等其他因素),但必须始终检查 null 作为返回值。

© www.soinside.com 2019 - 2024. All rights reserved.