C编译器是否应警告' char c = NULL'?

时间:2014-04-24 15:33:43

标签: c gcc casting null gcc-warning

使用这个简短的C文件 nulltest.c ,打印"嘿":

#include <stddef.h>
#include <stdio.h>

int main() {
  char c = NULL;
  c = 'e';
  printf("H%cy\n", c);
  return 0;
}

我的理解是在C中,NULL should expand to a null pointer constant,这会使char c = NULL隐式转换为整数指针。但是,在gcc 4.8中编译时这样做:

$ gcc --version
gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
$ gcc -Wall -Wconversion nulltest.c
$

我没有收到任何警告。

另一方面,先前版本的gcc和clang都警告相同的代码:

$ gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)
$ gcc nulltest.c
nulltest.c: In function ‘main’:
nulltest.c:5: warning: initialization makes integer from pointer without a cast

在Mac OS X 10.9上:

$ clang --version
Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)
$ clang nulltest.c
nulltest.c:5:8: warning: incompatible pointer to integer conversion initializing
      'char' with an expression of type 'void *' [-Wint-conversion]
  char c = NULL;
       ^   ~~~~

对于gcc 4.4和4.8,我认为relevant line in the relevant copy of stddef.h读取#define NULL ((void *)0)

为什么一个版本的gcc警告而不是另一个?

1 个答案:

答案 0 :(得分:8)

他们应该警告吗?当然。他们需要吗?否。

空指针常量不一定是指针类型;实际上它通常不是。 (是的,这和你想象的一样奇怪。)

空指针常量是值为0的常量整数表达式,或者是转换为void*的表达式。所以实现可能有:

#define NULL 0

不幸的是,这意味着它不会在

上报告错误
char c = NULL;

实施也可以:

#define NULL ((void*)0)

可以避免这个特定问题 - 但并非所有实现都这样做。 (旁注:在C ++中,((void*)0)不是有效的空指针常量。)

编译器可以使用其他技巧来检测此类逻辑错误。例如:

enum { __NULL__ };
#define NULL __NULL__

甚至:

#define NULL __magic_builtin_null_pointer_constant__

NULL仍然扩展为类型为int的表达式,值为零,但编译器可能会检测到此特定表达式的使用(如果它在内部保留该信息)并发出警告。 / p>

但最终,作为一名程序员,您可以避免这种特殊错误。不幸的是,你不能依靠编译器来为你检测它。

另一个含义是,如果需要将空指针作为参数传递给具有可变数量参数的函数,则不能安全地传递NULL;你必须将它强制转换为适当的指针类型。 execl*()函数是最常见的例子。

标准定义了术语空指针常量,但它没有说空指针常量是具有指针类型的表达式。表达式0始终是int类型 - 并且它总是一个空指针常量,即使在非指针上下文中使用,如

int n = 0;

(并且它是一个八进制常量。)术语空指针常量需要被理解为由标准定义的单个概念,而不是作为其含义源自其标准的短语组成词。