【问题标题】:Should C compilers warn on 'char c = NULL'?C 编译器应该警告'char c = NULL'吗?
【发布时间】:2014-04-24 15:33:43
【问题描述】:

获取这个简短的 C 文件 nulltest.c,它会打印“Hey”:

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

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

我的理解是,在 C 语言中,NULLshould 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 会发出警告而不是另一个?

【问题讨论】:

  • 只是我对此的评论:GCC 显然不好,如果不是不合适的话,我会使用不同的词。
  • gcc -E nulltest.c | tail -n 6 产生什么?
  • “NULL 应该扩展为一个空指针常量”:非常正确。但是,如果你看一下标准中“空指针常量”的定义,你会发现空指针常量并不保证有指针类型。
  • gcc (Debian 4.7.2-5) 4.7.2 确实发出警告。

标签: c gcc casting null gcc-warning


【解决方案1】:

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

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

空指针常量要么是值为 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 类型的表达式,但编译器可能会检测到此特定表达式的使用(如果它在内部保留该信息)并发出警告。

但最终,作为一名程序员,避免这种特殊错误最终取决于您。不幸的是,您不能依赖编译器为您检测它。

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

标准定义了术语空指针常量,但它并没有说空指针常量是具有指针类型的表达式。表达式0 始终是int 类型——并且它始终是一个空指针常量,即使在像

这样的非指针上下文中使用也是如此
int n = 0;

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

【讨论】:

  • 这很有帮助。这个后续有点荒谬,但是:从形式上讲,将NULL 扩展为0(表示空指针常量,而不是int)然后将0 解释为int 与值零而不是空指针常量? (C 标准 defines 一个“值为 0 的整数常量表达式”作为空指针常量(在 pointer contexts 中),而不是相反。)换句话说,char c = NULL 应该不等于 char c = 0,对?
  • @duozmo:查看我的更新答案;我刚刚添加了最后 3 段。
  • 好的,再次感谢基思。我还向那些从不同角度想要这个问题的人推荐这个问题和你的答案:Can a conforming C implementation #define NULL to be something wacky?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多