(int *)0 是空指针吗?

Posted

技术标签:

【中文标题】(int *)0 是空指针吗?【英文标题】:Is (int *)0 a null pointer? 【发布时间】:2014-02-18 15:41:22 【问题描述】:

这可以被认为是this question 的扩展(我只对 C 感兴趣,但添加 C++ 来完成扩展)

6.3.2.3.3 中的 C11 标准说:

值为 0 的整数常量表达式,或转换为 void * 类型的此类表达式称为空指针常量。

我个人对此的看法是 0(void *)0 代表空指针,其整数值实际上可能不是 0,但这不包括将 0 转换为任何其他类型。

但是,标准继续:

如果将空指针常量转换为指针类型,则生成的指针,称为空指针,...

(int *)0 覆盖为空指针,因为强制转换是显式转换(C11,6.3),它列在转换方法下。

然而,让我好奇的是下面这句话

...或这样的表达式转换为void * ...

有了上面的语义,这句话​​似乎完全没用了。问题是,这句话完全没用吗?如果不是,它有什么影响?因此,(int *)0 是否为空指针?


另一个可以帮助讨论的问题如下。 (long long)123 是否被视为“123 转换为 long long”,或“123 类型为 long long”。也就是说(long long)123有转化吗?如果没有,那么上面的第二个引用不会(int *)0 覆盖为空指针。

【问题讨论】:

可能想看看***.com/questions/2597142/… (int*)0a 空指针,而不是 @DavidRodríguez-dribeas,你是对的,但是由于 任何两个空指针都应该比较相等 (C11, 6.3.2.3-4),它并不会真正产生多大影响区别他们之间的区别。 你可能想在这里看看我的问题:***.com/questions/3889541/… @Shahbaz:嗯,这一切都取决于。空 int* 是否与空 double* 相同?我猜在 C 中就是这样...... 【参考方案1】:

计算表达式(int*)0 会产生一个int* 类型的空指针。

(int*)0 不是 空指针常量

空指针常量是一种特殊的表达式,可能出现在 C 源代码中。 空指针是一个可能出现在正在运行的程序中的值。

C 和 C++(作为两种不同的语言)在这方面的规则略有不同。 C++ 没有“或这样的表达式转换为类型void*”的措辞。但我认为这不会影响对您问题的回答。

至于你关于(long long)123 的问题,我不确定它是如何相关的,但是表达式123 的类型是int,并且演员表指定了从intlong long 的转换。

我认为核心混淆是假设(int*)0 中的转换没有指定转换,因为0 已经是一个空指针常量。但是 空指针常量 不一定是指针类型的表达式。特别是,表达式0 既是一个空指针常量,也是一个int 类型的表达式;它不是任何指针类型。 空指针常量这个术语需要被视为一个单一的概念,而不是一个短语,其含义取决于组成它的单个单词。

【讨论】:

但是(在 Windows 7 下使用 GCC-4.8.1/MinGW-32)(int *)0 == NULL 的计算结果似乎为 1(即 true)。所以它确实比较等于一个空指针常量。这是标准还是实现定义的? 我修复了NULL 的情况。但这是您对[language-lawyer] 标签的回答,还是在开始阅读答案之前我应该​​期望每分钟进行 10 次编辑? @Kninnug 是的,这是标准的,但由于标准的不同部分:== 的定义表示所有空指针比较相等,无论它们的精确类型如何。 @KeithThompson,对不起,我看到了一个两行的答案(这不是很精确和标准的引用!)我想你可能是其中之一 fastest gun in the west 人。跨度> @n.m.,我对此没有异议。但是(int *)0 是空指针吗?基思说是的,但没有给出任何参考或推理。他也可能只是发表他的个人意见。【参考方案2】:

简答:

在 C 和 C++ 中,(int *)0 是一个常量表达式,其值为空指针。然而,它不是一个空指针常量。据我所知,其值为空指针的常量表达式和空指针常量之间唯一可观察到的区别是空指针常量可以分配给任何指针类型,但其值是空指针的常量表达式具有特定的指针类型,并且只能分配给具有兼容类型的左值。在 C 而不是 C++ 中,(void *)0 也是一个空指针常量;这是void * 的一个特殊情况,与void * 与任何其他指向对象类型的赋值兼容的一般C-but-not-C++ 规则一致。

例如:

long *a = 0;           // ok, 0 is a null pointer constant
long *b = (long *)0;   // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0;   // ok in C, invalid conversion in C++
long *d = (int *)0;    // invalid conversion in both C and C++

在这种情况下,空指针常量 (void *)0 和类型为 void * 的常量表达式的值是空指针的差异是可见的,即使在 C 语言中也是如此:

typedef void (*fp)(void);  // any pointer-to-function type will show this effect

fp a = 0;                  // ok, null pointer constant
fp b = (void *)0;          // ok in C, invalid conversion in C++
fp c = (void *)(void *)0;  // invalid conversion in both C and C++

此外,现在它还没有实际意义,但既然你提出来了:无论如何long * 的空指针的位表示是,所有这些断言的行为都如 cmets 所示:

// 'x' is initialized to a null pointer
long *x = 0;

// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);

assert (x == 0);         // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified behavior in C++
assert (x == (int *)0);  // invalid comparison in both C and C++

assert (memcmp(&x, &y, sizeof y) == 0); // unspecified

assert (y == 0);         // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x);         // UNDEFINED BEHAVIOR: y may be a trap representation

“未指定”比较不会引发未定义的行为,但标准并没有说明它们评估的是真还是假,并且实现不需要记录两者中的哪一个,甚至选择一个并坚持它。如果您多次调用,上面的memcmp 在返回 0 和 1 之间交替是完全有效的。


带有标准引号的长答案:

要了解 空指针常量 是什么,您首先必须了解 整数常量表达式 是什么,这很麻烦 - 完整的理解需要您详细阅读 C99 的第 6.5 和 6.6 节。这是我的总结:

常量表达式是任何 C 表达式,编译器可以在不知道任何对象的值(const 或其他方式)的情况下将其计算为常量;但是,enum 值是公平的游戏),并且没有副作用。 (这是对大约 25 页标准语的大幅简化,可能并不准确。)

整数常量表达式是常量表达式的一个受限子集,方便地在一个段落中定义,C99 6.6p6 及其脚注:

整数常量表达式96 应该是整数类型,并且只能有整数常量、枚举常量、字符常量、sizeof 表达式的结果是整数常量的操作数和浮点常量强制转换的直接操作数。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,除非作为 sizeof 操作数的一部分 运算符。

96 整数常量表达式用于指定结构的位域成员的大小、枚举常量的值、数组的大小或 case 的值持续的。 6.10.1 中讨论了适用于 [#if] 中使用的整数常量表达式的进一步约束。

就本次讨论而言,重要的一点是

强制转换运算符 ... 只能将算术类型转换为整数类型

这意味着(int *)0 不是一个整数常量表达式,尽管它是一个常量表达式。

C++98 的定义似乎或多或少是等价的,模 C++ 特性和与 C 的偏差。例如,C++ 中字符和布尔类型与整数类型的更强分离意味着 C++ 标准谈到“integral 常量表达式”而不是“integer 常量表达式”,然后有时不仅需要一个整型常量表达式,还需要一个整型常量表达式整数类型,不包括charwchar_tbool(也许还有signed charunsigned char?我从文中不清楚)。

现在,空指针常量的 C99 定义就是这个问题的全部内容,所以我再重复一遍:6.3.2.3p3 说

值为 0 的整数常量表达式,或这种类型的表达式 void *,称为空指针常量。如果将空指针常量转换为 指针类型,生成的指针,称为空指针,保证与指向任何对象或函数的指针比较不相等。

Standardese 非常非常直白。这两句话的意思完全一样:

值为 0 的整数常量表达式称为 空指针常量。 一个值为 0 的整数常量表达式,转换为 void * 类型,也是一个 空指针常量。 当any空指针常量转换为指针类型时,得到的指针称为空指针,保证比较不等...

(斜体 - 术语的定义。粗体 - 我的重点。)所以这意味着,在 C 中,(long *)0(long *)(void *)0 是完全相同的两种写法,即类型为 @987654347 的空指针@。

C++ 不同。等效文本为 C++98 4.10 [conv.ptr]:

空指针常量是整数类型的整数常量表达式 (5.19) 右值,其计算结果为零。

就是这样。 “整数类型的整型常量表达式右值”与 C99 的“整型常量表达式”几乎相同,但有一些东西在 C 中符合条件但在 C++ 中不符合条件:例如,在 C 中,字符文字 '\x00' 是整数常量表达式,因此是一个空指针常量,但是在 C++ 中它不是整数类型的整数常量表达式,所以它也不是一个空指针常量。

更重要的是,C++ 没有“或这样的表达式转换为void *”子句。这意味着 ((void *)0) 不是 C++ 中的空指针常量。它仍然是一个 空指针,但它与任何其他指针类型的赋值都不兼容。这与 C++ 通常比较挑剔的类型系统是一致的。

C++11(但不是,AFAIK,C11)修改了“空指针”的概念,为它们添加了一个特殊类型(nullptr_t)和一个计算为空指针常量的新关键字(nullptr )。我不完全理解这些变化,也不打算解释它们,但我很确定裸露的0 在 C++11 中仍然是一个有效的空指针常量。

【讨论】:

如果您能提及您正在谈论的标准,即 C 或 C++,我将不胜感激。另外,你能举一个可以有(void *)0但不能有(int *)0的常量表达式的例子吗? C,特别是 C99;我还没有读过 C11,我不记得 (void *)0 是否仍然是 C++ 中的空指针 constant。 C 和 C++ 在这方面存在 差异,例如,NULL 在 C++ 中不允许扩展为 ((void *)0) @Zack C 和 C++ 都要求 NULL 是一个空指针常量。在 C99 中,这包括 (void *)0,但 C++ 将空指针常量定义为“整数类型的整数常量表达式右值,其计算结果为零。” @DKrueger 我不记得 C++ 是否从空指针常量的定义中排除了 ((void *)0),或者它只是说不允许 NULL 扩展到该表达式。好像是前者。 我不确定为什么fp c = (void *)(void *)0 无效。谁能解释一下?

以上是关于(int *)0 是空指针吗?的主要内容,如果未能解决你的问题,请参考以下文章

什么是空指针,什么是空指针?

用C语言怎么判断一个int变量是空还是为0

Objective-C09-空指针和野指针

队列练习之Example004-设计一个循环队列,用 front 和 rear 分别作为队头和队尾指针,另外用一个标志 tag 表示队列是空还是不空

Objective-C 空指针和野指针

有关C语言指针的问题:为啥最后结果是空指针?