C 在调用已知检查参数本身的函数之前检查参数

C 在调用已知检查参数本身的函数之前检查参数,c,function,styles,C,Function,Styles,我想这是一个代码风格和传统的问题,但问题如下 假设我们有一个检查参数的函数,例如: int do_something(int * arr) { if (arr == NULL) { printf("arr is NULL\n"); return -1; } ... 现在我们将使用它问题是:我们是否应该检查要传递给它的论点?因为它无论如何都会检查的 这个例子相当简单,现实生活中的场景可能要困难得多,调用函数会产生更多的参数(和更复杂的检查)

我想这是一个代码风格和传统的问题,但问题如下

假设我们有一个检查参数的函数,例如:

int do_something(int * arr) {
    if (arr == NULL) {
        printf("arr is NULL\n");
        return -1;
    }
    ...
现在我们将使用它问题是:我们是否应该检查要传递给它的论点?因为它无论如何都会检查的

这个例子相当简单,现实生活中的场景可能要困难得多,调用函数会产生更多的参数(和更复杂的检查)和更多额外的开销——如果这是某种IPC的话


所以更普遍的问题是:对于这种情况,什么是共同的指导方针和实践?

这将基于个人经验和偏见。不管怎样,这里是:

  • 如果
    do\u something
    属于某个库,并且作为一个用户可以直接调用的函数公开给该库的用户,最好进行所有必要的检查,以确保
    do\u something
    是健壮的

  • 如果它是库中的一个内部函数,那么最好进行所有必要的检查,以确保
    do\u something
    是健壮的。如果程序/库的性能受到检查的不利影响,则我只会进行检查,而不会进行检查

  • 如果它是您自己应用程序中的一个函数,那么最好进行所有必要的检查,以确保
    dou\u something
    是健壮的。如果程序/库的性能受到检查的不利影响,则我只会进行检查,而不会进行检查


  • 这将基于个人经验和偏见。不管怎样,这里是:

  • 如果
    do\u something
    属于某个库,并且作为一个用户可以直接调用的函数公开给该库的用户,最好进行所有必要的检查,以确保
    do\u something
    是健壮的

  • 如果它是库中的一个内部函数,那么最好进行所有必要的检查,以确保
    do\u something
    是健壮的。如果程序/库的性能受到检查的不利影响,则我只会进行检查,而不会进行检查

  • 如果它是您自己应用程序中的一个函数,那么最好进行所有必要的检查,以确保
    dou\u something
    是健壮的。如果程序/库的性能受到检查的不利影响,则我只会进行检查,而不会进行检查


  • 如果函数记录为检查参数,则在不检查的情况下调用它。例如,
    free()
    被明确地记录为接受
    NULL
    作为参数,因此,编写太常见的

    if(p != NULL)
      free(p);
    
    它会把你的代码弄得乱七八糟,毫无益处。避免不必要的呼叫的好处通常在噪音中完全消失

    当然,您必须检查函数的返回值

    如果您是函数的实现者,无论是库函数还是局部帮助函数,甚至可以在某些编译器上注释函数的特定约定

    例如,如果您的函数不接受
    NULL
    指针,则可以(在gcc上)在函数声明时设置
    \uuuuuuu属性((非NULL))

    例如,如果您声明:

    int do_something(int * arr) __attribute__((nonnull (1)));
    

    您将知道不能使用
    NULL
    调用函数(如果编译器知道您这样做,它甚至会发出警告)。但要小心,这种特性有时会让人大吃一惊。如果您像我一样声明它,并且仍然像在您的示例中那样检查参数,那么您可能会发现优化器完全从目标代码中删除检查。

    如果函数记录为检查参数,则在不检查的情况下调用它。例如,
    free()
    被明确地记录为接受
    NULL
    作为参数,因此,编写太常见的

    if(p != NULL)
      free(p);
    
    它会把你的代码弄得乱七八糟,毫无益处。避免不必要的呼叫的好处通常在噪音中完全消失

    当然,您必须检查函数的返回值

    如果您是函数的实现者,无论是库函数还是局部帮助函数,甚至可以在某些编译器上注释函数的特定约定

    例如,如果您的函数不接受
    NULL
    指针,则可以(在gcc上)在函数声明时设置
    \uuuuuuu属性((非NULL))

    例如,如果您声明:

    int do_something(int * arr) __attribute__((nonnull (1)));
    

    您将知道不能使用
    NULL
    调用函数(如果编译器知道您这样做,它甚至会发出警告)。但要小心,这种特性有时会让人大吃一惊。如果您像我一样声明它,并且仍然像在示例中那样检查参数,那么您可能会发现优化器完全从目标代码中删除了检查。

    只有一个答案:这取决于。您可能应该在调用者中进行合理的测试。如果调用函数位于同一个翻译单元(或使用LTO),则可以通过编译器优化复制的测试,如果调用的函数不在同一个翻译单元中,就不必检查函数原型来调用它。@ MichaelWalz,但我应该注意和考虑什么,然后呢?环境。你的真实生活情况。这是个问题。为函数定义的前置和后置条件将帮助您进行调用。在许多情况下,对双方进行检查(对输入进行消毒)是一种很好的设计实践,可以最大限度地降低调用方不遵守合同的风险