C - 制作API时的NULL检查?

时间:2017-06-03 07:12:28

标签: c null

假设您正在为某种目的制作一个相当强大且经过严格测试的API ...就像数据结构一样。其他人将在他们自己的编码项目中下载和使用您的库,因此您希望它非常紧密。

在您的API中,您有许多这样的函数调用:

void remove(struct myDataStruct *s, void* element_to_be_removed)
{
    if (element_to_be_removed == NULL)
       // Output an error, not allowed to enter NULL.
    else if (isEmpty(s))
       // Output an error, cannot remove something from empty structure.
    else
    {
       // Everything is safe.
       // Begin removing the element to the data structure.
    }
}

添加以下内容:

if (s == NULL)
   // Output an error, data structure initialized by user is NULL.

如果我决定检查这一点,那么在将数据结构作为参数接受的每个API函数调用中,我都必须包含该检查。这是不好的做法吗?完全没必要?关于那种NULL检查的一般共识是什么?如果我没有检查NULL那么有人可以这样做:remove(NULL, NULL)我的图书馆会崩溃他的程序。

`

3 个答案:

答案 0 :(得分:3)

我想这取决于API客户端是谁。在我工作的地方,通常我们只是在函数顶部使用assert(whatever!=NULL);,并且有一个版本和一个调试版本。

但我想你可以做另一件事,比如不返回void你可以返回包含输出代码的int。例如,如果有错误则返回负数,如果成功则返回0。有很多C API就是这样做的。

在我看来,如果您不需要超极简主义的应用程序,并且您不确定API调用者是否会使用正确的参数调用方法,您可以将其添加为额外的检查。否则,如果API客户端没有完全检查传递的参数是否正常,则需要长时间的调试。

答案 1 :(得分:3)

正如评论中所述,这个问题有很多方法。我将解释一个广泛使用的,以及它的好处。

怎么做,例如/伪代码

void remove(struct myDataStruct *s, void* element_to_be_removed)
{
    // pre-conditions:
    assert(s != NULL);
    assert(element_to_be_removed != NULL);
    assert(!isEmpty(s));

    // implementation:
    [...]

    // optional post-conditions
    // assert(s != NULL); <- maybe something more sensible
}

现在,这种方法的优点是什么:

  • 效率。在发布版本中,所有这些assert()都将消失。在调试版本中,他们将检查方法的合同是否遵循,否则立即停止,从而帮助您的图书馆用户找到他出错的地方。
  • 可读性。您明确将合同检查与业务逻辑分开。检查前置条件发生在方法的前几行,仅包含assert() s。

当然,这仅适用于通过例如参数NULL被视为调用代码中的错误。大多数情况下,情况确实如此。甚至不会尝试发出有意义的错误并以某种方式恢复,而是快速失败。错误是一个错误,继续执行程序没有意义,这只会使调试变得复杂。

如果传递NULL是意料之外的,但实际上并不是一个错误,那么你应该去&#34;记录&#34;改为接近。在您的示例中不是这种情况。

答案 2 :(得分:2)

我的回答非常基于意见:

不,你没有。

当您致电某项功能时,必须验证是否有错误。您此时检查数据。例如:

int i;
int ret = scanf("%d", &i);
if (ret != 1) {
  return ERROR;
}
// here `i` is valid

如果您多次检查数据的有效性,这是没用的。为此,API不应检查指针是否为NULL,因为用户应该在需要时完成它。

int main(void) {
  int *api_data = malloc(sizeof *api_data);
  if (api_data == NULL) {
    return 1;
  }

  int ret = init_api_data(api_data); // api_data is valid
  if (ret == ERROR) {
    free(api_data);
    return 1;
  }
  free(api_data);
}

int init_api_data(int *api_data) {
  if (api_data == NULL) { // This is useless api_data has already been checked
    return ERROR;
  }
  return OK;
}

这是C的理念,信任用户。当然,这会导致错误,这就是为什么我们试图创造一种更好的语言(例如,锈,哈斯克尔)。