如何为我的自定义函数生成非null参数的警告?

我正在尝试编写一个库函数,其中参数不应为null,并希望gcc在某些人尝试传递NULL时应生成警告。 我的代码是

#include  #include  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,它用于生成警告。 但对于我的function,它不是。

 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)); ^ 

或许声明GCC nonnull函数属性是您正在寻找的。 您可以指定所有指针参数都应为非null,或者您可以提供一个参数列表,指定哪些参数应为非null。

我读取文档的方式,如果声明了nonnull属性,则将空指针传递给非null参数是未定义的行为。 必须启用-Wnonnull选项才能获得警告。 启用警告后,如果将NULL传递给非null参数,则会发出警告。 但是, 不会对传递的空指针变量发出警告 。 因此,在运行时处理错误输入很重要。 感谢@alk指出这个重要问题。

看起来Clang也支持这个属性 。 Clang还具有可空性类型限定符_Nonnull ,但这在GCC中不可用。

我应该强调的是,C标准不支持这种行为,而是编译器扩展。 为了便于携带,您应该在运行时通过精心设计的function处理错误的输入。

这是使用属性声明的函数(没有NULL指针检查的早期版本)。 我添加了一行调用own_strcmp()并使用空指针变量; 请注意,此行不会导致发出警告。 另请注意,使用空指针变量调用标准库strcmp()无法触发警告。

 #include  #include  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]; } 

这是调用GCC后的输出:

 λ> gcc -std=c11 -Wall -Wextra -Wpedantic warning_gcc_42035769.c warning_gcc_42035769.c: In function 'main': warning_gcc_42035769.c:13:5: warning: null argument where non-null required (argument 1) [-Wnonnull] printf("%s and %s is %d\n", str1, str2, own_strcmp(NULL, str2)); ^ 

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  void foo(int p[static 1]) void bar(void) { foo(NULL); } 

使用带有默认开关的Apple LLVM 9.0.0(clang-900.0.39.2),编译器会发出警告:

 xc:5:18: warning: null passed to a callee that requires a non-null argument [-Wnonnull] void bar(void) { foo(NULL); } ^ ~~~~ xc:3:14: note: callee declares array parameter as static here void foo(int p[static 1]) {} ^~~~~~~~~~~ 

但是,此代码编译时没有警告:

 #include  void foo(int p[static 1]) {} void bar(int *p) { foo(p); } void baz(void) { bar(NULL); } 

所以看起来这个编译器只有在直接传递时才能检测到这个参数错误地传递了null。