从C17草案(6.3.2.3¶3):
值为0的整数常量表达式,或转换为void*
类型的表达式,称为空指针常量。67)如果将空指针常量转换为指针类型,则保证生成的指针(称为空指针)与指向任何对象或函数的指针进行比较。
67)宏NULL
在中定义
由此可见,以下是空指针常量:0
、0UL
、(void*)0
、(void*)0UL
、NULL
。
进一步得出以下是空指针:(int*)0
、(int*)0UL
、(int*)(void*)0
、(int*)(void*)0UL
、(int*)NULL
。有趣的是,这些都不是“空指针常量”;见这里。
以下空指针常量是空指针(因为void*
是指针类型,0
和0UL
是空指针常量):(void*)0
,(void*)0UL
。在这方面,根据C17草案(6.2.5¶19-20):
void
类型包含一组空值;它是一个不完整的对象类型,无法完成。
[…]
指针类型可以从函数类型或对象类型派生,称为引用类型。[…]指针类型是一个完整的对象类型。
void
本身不是指针类型,它是一个不完整的对象类型。但是void*
是一种指针类型。
但似乎以下是空指针常量,它们不是空指针(因为没有对指针类型的强制转换):0
、0UL
、NULL
。(准确地说,虽然标准只要求将NULL
定义为“空指针常量”,但允许将其定义为空指针常量,它也是空指针。但似乎标准不要求NULL
以同时是空指针的方式定义。)
每个空指针常量都是空指针吗?(NULL
真的不是空指针吗?)
最后(有点开玩笑):如果某些空指针常量不是空指针,它们在技术上是一种“非空指针”吗?(这种措辞出现在标准的某些地方。)请注意,从语言上讲,我们有一个所谓的括号悖论;我们可以将其理解为“[非空]指针”或“非[空指针]”。
每个空指针常量都是空指针吗?
太长别读。
正如您已经观察到的,值为0的整数常量表达式是空指针常量,尽管没有指针类型。您还引用了规范对空指针的定义:“转换为指针类型的空指针常量[]”。这意味着这种一般形式的空指针常量…
(void *)(<integer constant expression with value 0>)
…满足“空指针”的定义。整数常量表达式本身就是空指针常量,因此强制转换使整体表达式成为空指针(除了是空指针常量之外)。
另一方面,采用值为0的整数常量表达式形式的空指针常量不满足“空指针”的定义,语言规范中没有其他条款会使它们成为空指针。例如:0
、0x00UL
、1 2 3-6
。
该标准似乎不需要以这样一种方式定义NULL,即它同时是一个空指针。
正确。
每个空指针常量都是空指针吗?
当然不是(见上文),但对于大多数目的来说,这并不重要。
(NULL
真的不是空指针吗?)
这取决于您的C实现。语言规范允许任一答案。实际上,在您可能遇到的大多数实现中,它是一个空指针。
如果某些空指针常量不是空指针,它们在技术上会是一种“非空指针”吗?
不。不是空指针的空指针常量根本不是指针。它们是整数。
每个空指针常量都是空指针吗?
不,原因在你引用的文本中:
如果将空指针常量转换为指针类型,则保证生成的指针(称为空指针)与指向任何对象或函数的指针进行比较不相等。
空指针常量不会自动成为指针,就像任何整数常量不会自动成为指针一样。常量值必须转换为指针类型才能产生空指针。
生成的空指针不必是零值。它只需要是一个不能是任何对象或函数地址的值。该值可以是0x00000000
(在我熟悉的实现中),也可以是0xFFFFFFFF
,或者它可以是0xDEADBEEF
,或者它可以是其他东西。
空指针常量可以是void*
或某种整数类型。
在您的机器上测试:
#include <stdio.h>
#include <stdlib.h>
#define NULL_TEST(n) _Generic((n), \
void *: "void *", \
int: "int", \
long: "long", \
default: "something else" \
)
int main(void) {
printf("%s\n", NULL_TEST(NULL));
printf("%s\n", NULL_TEST((void*)0));
printf("%s\n", NULL_TEST(0));
printf("%s\n", NULL_TEST(0L));
}
在我的机器上,我有以下输出。您的输出可能会因第一行而异。
void *
void *
int
long