编译以下代码:
#include <string.h>
#define FOO (NULL)
int main(int argc, char *argv[])
{
char *foo;
if (FOO)
foo = strdup(FOO);
return 0;
}
导致以下编译器警告:
foo.c: In function ‘main’:
foo.c:9:3: warning: null argument where non-null required (argument 1) [-Wnonnull]
foo = strdup(FOO);
^
但是,strdup
因FOO
检查而NULL
为if (FOO)
,系统不会调用{{1}}。有没有办法避免这种警告?
谢谢!
答案 0 :(得分:4)
如果想要定义foo
,如果定义了FOO
,您可以尝试:
//#define FOO "lorem ipsum"
int main()
{
char *foo;
#ifdef FOO
foo = strdup(FOO);
#endif
}
它还有一个优点,即在不需要时不包括整个if
代码。
答案 1 :(得分:1)
您使用子句保护strdup
的调用是正确的,以确保永远不会使用strdup
参数调用NULL
。
但是,发出函数调用警告的编译器部分并不是知道调用永远不会发生的相同部分。
您可能会使用一个表达式来隐藏NULL
,以确保生成的参数表达式永远不会是NULL
。
e.g。
if (FOO) foo = strdup(FOO?FOO:"");
或
if (FOO) foo = strdup(FOO + !FOO);
这是&#34; clear&#34; (至少对编译器而言)strdup
不能使用NULL
值调用,而if
子句确保永远不会调用它NULL
#define NON_NULL(x) ((x)?(x):"")
价值。
在这一点上,我们挥挥手说,编译器将把它全部优化掉,并帮助我们在视觉上优化它,我们有:
#define NON_NULL(x) ((x)?(x):(abort(),""))
和调试版本,如:
?:
我们可能会使用GNU扩展(x)
(可选的缺少中间句子默认为第一个子句),以避免多次评估#define NON_NULL(x) ((x)?:"")
。
#define NON_NULL(x) ((x)?:(abort(),"")
和调试版本,如:
if (FOO) foo = strdup(NON_NULL(FOO));
现在你可以呈现一些在技术上更加模糊但显然更有意义的东西:
NON_NULL
并假装unittest.mock.patch
是一些正式的记法和承认。
答案 2 :(得分:0)
另一个策略是使用静态内联函数,我刚碰到同样的事情。所以这(在我看来)确实是一个编译问题,让我们考虑一下:
#define foo(v, x) do { v = x ? strdup(x) : NULL; } while(0)
使用 -W -Wall -Werror 这个
char *bar;
foo(bar, NULL);
将失败
nonnull.C:3:40: error: null argument where non-null required (argument 1) [-Werror=nonnull]
3 | #define foo(v, x) do { v = x ? strdup(x) : NULL; } while(0)
这显然是假的,因此,我个人确实认为这是编译器的一个缺点,因为编译器证明 x 在传递给 strdup 时不能为空应该是微不足道的,即使我们显式传递 NULL(整个分支应该被优化掉,因为获取代码的条件显式为 false)。
这(保持宏语义)效果更好:
static inline
void foo(char* &v, char* x) {
v = x ? strdup(x) : NULL;
}
但“改进的”语义也可能是:
static inline
char* foo(char* x) {
return x ? strdup(x) : NULL;
}
bar = foo(NULL);
我打赌这不会解决所有情况,但是可以将上面用作 strdup_if_not_null() 并在宏中使用它而不是 strdup()。是的,这是一个杂乱无章的东西,而且很讨厌,但确实有效。