我正在尝试编写一个库函数,其中参数不应该为null,并希望gcc在某些人尝试传递NULL时应该生成警告。我的代码是
#include <stdio.h>
#include <string.h>
int own_strcmp(const char *str1, const char *str2)
{
if( str1 == NULL || str2 == NULL){
if(str1 == NULL && str2 == NULL)
return 0;
else if( str1 == NULL)
return str2[0];
else
return str1[0];
}
int i=0;
while(str1[i] && str2[i]){
if( str1[i] != str2[i]){
break;
}
i++;
}
return str1[i]-str2[i];
}
int main(int argc, char *argv[]){
const char *str1 = "hello";
const char *str2 = "hello";
printf("%s and %s is %d\n", str1, str2, own_strcmp(NULL, str2));
printf("%s and %s is %d\n", str1, str2, strcmp(NULL, str2));
return 0;
}
用于标准库的strcmp,用于生成警告。但对于我的功能,它不是。
rajesh@ideapad:~$ gcc own_strcmp.c
own_strcmp.c: In function ‘main’:
own_strcmp.c:21:2: warning: null argument where non-null required (argument 1) [-Wnonnull]
printf("%s and %s is %d\n", str1, str2, strcmp(NULL, str2));
^
答案 0 :(得分:4)
也许宣称GCC nonnull
function attribute正是您要找的。您可以指定所有指针参数都应为非null,或者您可以提供一个参数列表,指定哪些参数应为非null。
我读取文档的方式,如果声明了Arraylist
属性,则将空指针传递给非null参数是未定义的行为。必须启用nonnull
选项才能收到警告。启用警告后,如果将-Wnonnull
传递给非null参数,则会发出警告。但是,对于传递的空指针变量不会发出警告。因此,在运行时处理错误输入很重要。感谢@alk指出这个重要问题。
看起来像Clang also supports this attribute。 Clang还具有可空性类型限定符NULL
,但这在GCC中不可用。
我应该强调的是,C标准不支持这种行为,而是编译器扩展。为了便于携带,您应该在运行时通过精心设计的功能处理错误的输入。
这是使用属性声明的函数(没有_Nonnull
指针检查的早期版本)。我添加了一行调用NULL
并使用空指针变量;请注意,此行不会导致发出警告。另请注意,使用空指针变量调用标准库own_strcmp()
无法触发警告。
strcmp()
以下是调用GCC后的输出:
#include <stdio.h>
#include <string.h>
int own_strcmp(const char *str1, const char *str2)__attribute__((nonnull));
int main(void)
{
const char *str1 = "hello";
const char *str2 = "hello";
const char *arg = NULL;
/* Warning issued for this NULL argument */
printf("%s and %s is %d\n", str1, str2, own_strcmp(NULL, str2));
/* No warning issued for the NULL variable arg */
printf("%s and %s is %d\n", str1, str2, own_strcmp(arg, str2));
/* No warning issued for strcmp() either */
printf("%s and %s is %d\n", str1, str2, strcmp(arg, str2));
return 0;
}
int own_strcmp(const char *str1, const char *str2)
{
int i=0;
while(str1[i] && str2[i]){
if( str1[i] != str2[i]){
break;
}
i++;
}
return str1[i]-str2[i];
}
答案 1 :(得分:0)
C有一个声明指针参数的方法,该参数不能为null。但是,它不能保证永远不会传递空指针。它建议编译器可以编译例程,期望参数永远不会为null,但它不一定会阻止调用例程错误地传递null。编译器可能会检测到对此类例程的一些不正确的调用,并发出有关它们的警告或错误,但无法检测到其他例程。
这是通过将static
关键字放在参数声明中的很少使用的语法来完成的:
void foo(int p[static 1])
{
… body of function …
}
这表明p
必须提供对至少一个元素的数组的第一个元素的访问权限,根据C 2011 [N1570] 6.7.6.3 7.因为必须有一个元素p
点,p
可能不为空。例如,编译此代码时:
#include <stddef.h>
void foo(int p[static 1])
void bar(void) { foo(NULL); }
使用带有默认开关的Apple LLVM 9.0.0(clang-900.0.39.2),编译器警告:
x.c:5:18: warning: null passed to a callee that requires a non-null argument
[-Wnonnull]
void bar(void) { foo(NULL); }
^ ~~~~
x.c:3:14: note: callee declares array parameter as static here
void foo(int p[static 1]) {}
^~~~~~~~~~~
但是,此代码编译时没有警告:
#include <stddef.h>
void foo(int p[static 1]) {}
void bar(int *p) { foo(p); }
void baz(void) { bar(NULL); }
因此,只有在直接传递此参数时,此编译器才能检测到此类参数的错误传递。