简短回答:
在C和C++中,(int *)0
是一个值为 null 的常量表达式,它是一个空指针。然而,它不是一个 null 指针常量。据我所知,一个值为 null 的常量表达式和一个空指针常量之间唯一可观察的区别是,空指针常量可以赋值给任何指针类型的左值,但一个值为 null 的常量表达式具有特定的指针类型,只能赋值给具有兼容类型的左值。在C中,但不在C++中,(void *)0
也是一个空指针常量;这是对于 void *
的特殊情况,与一般的C但不适用于C++的规则一致,即 void *
可以与任何其他对象指针类型进行赋值兼容。
例如:
long *a = 0;
long *b = (long *)0;
long *c = (void *)0;
long *d = (int *)0;
这里有一个案例,可以看到在C语言中,空指针常量
(void *)0
和一个值为null的具有类型
void *
的常量表达式之间的区别。
typedef void (*fp)(void);
fp a = 0;
fp b = (void *)0;
fp c = (void *)(void *)0;
此外,现在这个问题已经没有意义了,但既然你提到了:
无论long *
的空指针的位表示是什么,所有这些断言都会按照注释所示的方式运行。
long *x = 0;
long *y;
memset(&y, 0, sizeof y);
assert (x == 0);
assert (x == (long *)0);
assert (x == (void *)0);
assert (x == (int *)0);
assert (memcmp(&x, &y, sizeof y) == 0);
assert (y == 0);
assert (y == x);
"未指定"的比较不会引发未定义行为,但标准并未说明它们是评估为真还是假,并且实现不需要记录其中之一,甚至选择一个并坚持使用。如果您多次调用上述的
memcmp
,它交替返回0和1也是完全有效的。
长篇回答,带有标准引用:
要理解什么是“空指针常量”,首先你必须理解什么是“整数常量表达式”,而这个概念相当复杂——完全理解需要详细阅读C99的6.5和6.6节。以下是我的总结:
常量表达式是编译器可以在不知道任何对象的值(无论是const还是其他)的情况下计算出一个常量的任何C表达式,并且没有副作用。(这是对大约25页标准的极简化描述,可能不完全准确。)
整数常量表达式是常量表达式的一种受限子集,在C99 6.6p6及其脚注中方便地定义为一个段落:
整数常量表达式应具有整数类型,并且只能具有整数常量、枚举常量、字符常量、结果为整数常量的sizeof表达式以及作为强制转换的立即操作数的浮点常量作为操作数。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,除非它是sizeof运算符的操作数的一部分。
整数常量表达式用于指定结构体的位字段成员的大小、枚举常量的值、数组的大小或case常量的值。用于#if中使用的整数常量表达式的进一步约束在6.10.1中讨论。
对于本讨论而言,重要的是
强制转换运算符...只能将算术类型转换为整数类型
这意味着(int *)0不是整数常量表达式,尽管它是一个常量表达式。
C++98的定义似乎在某种程度上与C++功能和偏差等价。例如,C++中字符和布尔类型与整数类型的更强分离意味着C++标准使用"
整数常量表达式"而不是"
整型常量表达式",有时候要求的不仅仅是整数类型的整数常量表达式,还要排除
char
、
wchar_t
和
bool
(也许还包括
signed char
和
unsigned char
?从文本中我无法确定)。
现在,C99对于
空指针常量的定义正是这个问题的关键,所以我会重复一下:6.3.2.3p3说:
值为0的整数常量表达式,或者将这样的表达式转换为void *
类型,被称为空指针常量。如果将空指针常量转换为指针类型,得到的指针被称为空指针,保证与任何对象或函数的指针比较时都不相等。
Standardese非常、非常字面。这两个句子的意思与以下内容完全相同:
整数常量表达式值为0的被称为“空指针常量”。
整数常量表达式值为0,强制转换为类型“void *”,也是一个“空指针常量”。
当任何空指针常量转换为指针类型时,所得到的指针被称为“空指针”,并且保证不相等。
(斜体-术语定义。粗体-我的强调。)所以这意味着,在C中,“(long *)0”和“(long *)(void *)0”是写同样的东西的两种方式,即具有类型“long *”的空指针。
C++则不同。相应的文本是C++98 4.10 [conv.ptr]:
“空指针常量”是一个整数类型的积分常量表达式(5.19),其求值为零。
就这些。"整数类型的整数常量表达式右值"与C99的"整数常量表达式"几乎是一样的,但在C中有一些限定条件而在C++中没有:例如,在C中,字符字面值'\x00'
是一个整数常量表达式,因此是一个空指针常量,但在C++中它不是整数类型的整数常量表达式,所以也不是空指针常量。
更重要的是,C++中没有"或将此类表达式强制转换为void *
"的子句。这意味着((void *)0)
在C++中不是空指针常量。它仍然是一个空指针,但它与任何其他指针类型都不兼容。这与C++的类型系统通常较为严格的特点一致。
C++ 2011和C 2023都修订了"空指针"的概念,为其添加了一个特殊类型(nullptr_t)和一个新的关键字(nullptr),该关键字可用于表示空指针常量。我并不完全理解这些变化,也不打算试图解释它们,但我相当确定裸露的0在两者中仍然是一个有效的空指针常量。
(int*)0
是一个空指针,而不是唯一的。 - David Rodríguez - dribeasint*
空指针和double*
空指针是否相同?我想在 C 中是这种情况... - David Rodríguez - dribeas