我有一个API,其中一些函数是用nonnullGCC function属性声明的,例如:
// declaration in .h
__attribute__((nonnull))
int foo(const char *bar);
[...]
// definition in .c
int foo(const char *bar) {
if (bar == NULL)
return -1;
// do something with bar
...
}
字符串
但是NULL检查触发了**-Wnonnull-compareGCC警告。问题是nonnull**属性的文档说它:
使编译器检查调用[...]中的参数[...]是否为非空。
问题是它没有说明任何关于运行时的内容,所以如果我为了取悦GCC而删除这个检查,我可能会隐藏或在不必要的时候检测到调用程序中的错误。
那么在这种情况下,这个警告应该被静音吗?或者是文档中存在某种错误,使得没有检查的代码实际上是正确的?
FTR,我的编译器版本是11.4.0。
2条答案
按热度按时间mqkwyuun1#
-Wnonnull-compare警告是否具有误导性?
没有。the GCC 11.4 documentation中有一个错误。它给出了
nonnull
属性的一个示例,其中它说:例如,声明.使编译器检查在对
my_memcpy
的调用中,参数 dest 和 src 是否为非空。然而,该属性的非示例描述性文本说了一些不同的东西(强调是添加的):
它表示引用的参数必须是非空指针。
这在the GCC 12.3 documentation中得到了纠正,其中有关示例的文本更改为:
例如,声明.通知编译器,在对
my_memcpy
的调用中,参数 dest 和 src 是非空的。而非示例性描述性文本保持相同。
因此,
nonnull
属性是对编译器的Assert,它可以假设指针不为null,因此将非null指针与null进行比较的警告是正确的。总之,
nonnull
属性没有提供您想要的功能,即当为不应该为null的参数传递null指针时报告。oprakyz72#
使用
__attribute__((nonnull))
似乎完全等同于将函数声明为:int foo (const char bar[static 1])
的值。“在这两种情况下,您都与编译器编写了一个契约,上面写着“我保证这个参数永远不会是空指针,请相应地生成代码”。作为奖励,如果某些编译器可以在调用方推断传递了空指针,则它们还可能会给予诊断消息。
在检查vs NULL时,您会在函式内部得到诊断消息的原因是,您已经告诉编译器参数永远不会是null指标,所以检查是没有意义的。
如果你没有进行这种检查,编译器在调用端无法检测到一个传递的指针是空指针(可能是因为它发生在运行时),那么很可能会发生一些不好的事情,在这种情况下,这个属性并不是问题的解决方案。
那么,该怎么办呢?你已经在字里行间提出了正确的解决方案:
因此,如果我删除这个检查,以取悦GCC,我可能会隐藏,或检测到晚于必要的,在一个调用程序的错误。
调用程序中的错误应该会被调用程序检测到,这并不奇怪。因此,空指针检查应该在那里。理想的情况是尽可能接近指针由于某种原因可能变成空指针的情况。
正确用法示例:
字符串
错误用法示例:
型
值得注意的是:
ptr == NULL
检查每一个无关的库函数会造成100%无用分支的显著执行时间开销。特别是如果库是性能关键的,例如数据复制函数等,可能会从循环中重复调用。这种多余的错误检查是C语言中常见的非老手错误。