关于C语言中空指针常量与空指针的关系及相关标准疑问的技术问询
咱们先从C17标准草案的定义入手,直接看原文:
来自C17草案(6.3.2.3 ¶3):
一个值为0的整数常量表达式,或者被转换为void *类型的此类表达式,被称为空指针常量。67)如果一个空指针常量被转换为指针类型,得到的指针被称为空指针,它保证与任何指向对象或函数的指针比较不相等。67)宏
NULL在<stddef.h>(以及其他头文件)中被定义为空指针常量[...]。
从这个定义出发,我们可以明确这些空指针常量:
00UL(void *)0(void *)0ULNULL
而下面这些则是空指针:
(int *)0(int *)0UL(int *)(void *)0(int *)(void *)0UL(int *)NULL
有意思的是,这些空指针都不属于“空指针常量”的范畴——它们是常量表达式转换后的指针类型值,而非原始的空指针常量本身。
这里还要注意一个特殊情况:(void *)0和(void *)0UL既是空指针常量,也是空指针。因为void *本身是一种指针类型,咱们再看C17草案的补充说明(6.2.5 ¶19-20):
void类型包含空值集合;它是一种无法完成的不完整对象类型。
[...]
指针类型可以从函数类型或对象类型(被称为引用类型)派生而来。[...]指针类型是一种完整的对象类型。
简单总结:void不是指针类型,它是不完整类型;但void *是标准定义的指针类型,所以当空指针常量被转换为void *时,它同时具备了空指针常量和空指针的属性。
那反过来,0、0UL、NULL这些空指针常量,它们本身不是空指针——因为它们没有被转换为指针类型(严格来说,标准只要求NULL被定义为“空指针常量”,允许它同时也是空指针,但并没有强制要求必须这么定义)。
现在回到大家最关心的核心问题:
是不是每个空指针常量都是空指针?(
NULL真的不是空指针吗?)
答案是否定的。只有当空指针常量被转换为指针类型时,它才成为空指针。像0这种整数形式的空指针常量,本身是整数类型,不是指针;而NULL如果被定义为0或者0UL,那它也只是整数常量,不是指针——只有当它被转换为指针类型后,才变成空指针。如果某些空指针常量不是空指针,那它们算不算一种“非空指针”?
这里其实是个语言上的“括号悖论”:我们可以把“non-null pointer”理解成「[非空]指针」(即不是空指针的指针),或者「非[空指针]」(即不是空指针的任何值)。如果是前者,那这些非指针类型的空指针常量根本不属于“指针”范畴,自然谈不上是“非空指针”;如果是后者,那它们确实不是空指针,但这么表述容易造成混淆,标准里的“non-null pointer”应该特指前者——即指向有效对象/函数的指针,而非空指针的指针类型值。
备注:内容来源于stack exchange,提问作者Lover of Structure

