NULL
宏需要扩展为“实现定义的空指针常量” 。
空指针常量被定义为“一个值为 0 的整数常量表达式,或这种类型的表达式void *
”。与直觉相反,此定义不需要扩展NULL
为指针类型的表达式。一个常见的实现是:
#define NULL 0
空指针常量,在需要指针的上下文中使用时,可以隐式转换为指针值;结果是一个空指针。它也可以使用强制转换显式转换,例如(int*)NULL
.
但是没有要求只能在这样的上下文中使用符合空指针常量的表达式。这意味着如果实现选择NULL
如上定义,那么:
char c = NULL; // legal but ugly
是合法的并初始化c
为空字符。
这样的初始化是不可移植的(因为NULL
也可能扩展((void*)0)
和误导,所以应该避免,但是编译器很可能会在没有警告的情况下让它通过;NULL
被编译器的预处理阶段扩展到0
,后面的阶段会看到它as char c = 0;
,这是合法且无害的——尽管我个人更喜欢char c = '\0';
。
我刚刚在我自己的 32 位 Ubuntu 系统上使用 gcc 4.7 尝试了你的示例。在没有指定选项的情况下,编译器警告了p[2]=NULL;
和p[1]=(void *)0;
:
c.c:8:9: warning: assignment makes integer from pointer without a cast [enabled by default]
c.c:10:9: warning: assignment makes integer from pointer without a cast [enabled by default]
任何 C 编译器都会出现第二个警告;第一个表示NULL
实际定义为((void*)0)
(通过运行代码gcc -E
确认这一点)。
编译器并没有简单地“接受”这些赋值;它警告你他们。C 语言标准只要求对任何违反语言规则的行为进行“诊断”,甚至是语法错误;该诊断在法律上可能是非致命的警告信息。-std=c89 -pedantic-errors
您可以使用;使 gcc 的行为更加严格 替换或强制执行来自标准更高版本的规则c89
。(编辑:我从评论中看到您正在使用隐藏警告的编译器的 Web 界面;请参阅我对您的问题的评论以获取解决方法。警告很重要。)c99
c11
如果您发布产生编译器警告的 C 代码,请 向我们展示警告并自己密切关注它们。它们通常表明您的程序中存在严重问题,甚至是非法行为。
语言律师的狡辩:甚至不清楚:
char c = (void*)0;
指定从void*
到的转换char
。我自己的观点是,因为它违反了约束,所以它没有定义的语义。大多数不拒绝它的编译器会将其视为void*
转换char
,并且也有人认为这是必需的行为。但是,如果您只关注编译器警告和/或一开始不编写类似的代码,您就可以避免此类问题。
(C++ 的规则有点不同,但你问的是 C,所以我不会讨论这个问题。)