我在与a forum thread相关联的in a comment by @jsantander中阅读了此声明:
请记住,当您将指针指定或比较为零时,会在幕后发生一些特殊的魔法,以便为给定指针使用正确的模式(实际上可能不为零)。这就是
#define NULL (void*)0
之类的东西是邪恶的原因之一 - 如果你将char*
与NULL
比较魔法明确地(并且可能在不知不觉中)被关闭,并且可能会产生无效结果发生。只是为了更清楚:(my_char_ptr == 0) != (my_char_ptr == (void*)0)
所以我理解它的方式,对于一个架构,其中NULL指针是0xffff
,代码if (ptr)
,将ptr与0xffff
进行比较而不是0。 / p>
这是真的吗?它是用C ++标准描述的吗?
如果为true,则表示即使对于具有非零NULL指针值的体系结构,也可以安全地使用0。
修改
作为额外说明,请考虑以下代码:
char *ptr;
memset(&ptr, 0, sizeof(ptr));
if ((ptr == (void*)0) && (ptr != 0)) {
printf("It can happen.\n");
}
这就是我理解这个论坛帖子的主张。
答案 0 :(得分:34)
您的问题分为两部分。我将从:
开始如果为true,则表示即使对于具有非零NULL指针值的体系结构,也可以安全地使用0。
你正在混淆“价值”和“代表”。空指针的值称为空指针值。 表示是内存中用于存储此值的位。空指针的表示可以是任何东西,不要求它是全位零。
在代码中:
char *p = 0;
p
保证是空指针。它可能没有全位零。
这不比代码更“神奇”:
float f = 5;
f
与int 5
没有相同的表示形式(内存中的位模式),但没有问题。
C ++标准定义了这一点。在C ++ 11中,文本有所改变,增加了nullptr
;但是在所有版本的C和C ++中,转换为指针类型时的整数文字0
会生成空指针。
来自C ++ 11:
空指针常量是整数类型的整数常量表达式prvalue,其求值为零或类型为std :: nullptr_t的prvalue。空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与对象指针或函数指针类型的每个其他值区分开。这种转换称为空指针转换。
0
是空指针常量,而(char *)0
例如是char *
类型的空指针值。< / p>
空指针是否具有全位 - 零是无关紧要的。重要的是,当您将值0
的整数constexpr转换为指针类型时,保证生成空指针。
转到问题的另一部分。 您引用的文字是完整垃圾贯穿始终。正如我在上面所讨论的那样,类型之间的转换导致不同的表示形式的想法没有“神奇”。
保证代码my_char_ptr == NULL
能够测试my_char_ptr
是否为空指针。
如果您使用自己的源代码#define NULL (void*)0
编写,那将是邪恶的。这是因为定义可能由标准头定义的任何宏是未定义的行为。
但是,标准头文件可以编写任何他们喜欢的内容,以满足空指针的标准要求。编译器可以在标准头代码中“做魔术”;例如,文件系统上不必有名为iostream
的文件;编译器可以看到#include <iostream>
,然后硬编码标准要求iostream
发布的所有信息。但是出于显而易见的实际原因,编译器通常不会这样做;它们允许独立团队开发标准库。
无论如何,如果C ++编译器在其自己的头中包含#define NULL (void *)0
,并且因此发生了不符合的情况,那么编译器显然是不符合的。如果没有任何不符合要求,那就没有问题。
我不知道你引用的文字是谁会引导它的“是邪恶的”评论。如果针对编译器供应商告诉他们不要“邪恶”并且推出不合格的编译器,我想我们不能与之争辩。
答案 1 :(得分:12)
我认为您链接的论坛帖子不正确(或者我们误解了!=
的含义)。这两个子表达式具有不同的语义,但结果相同。假设my_char_ptr
确实具有类型char*
或类似的,以及有效值:
my_char_ptr == 0
将0
转换为my_char_ptr
的类型。这会产生一个空指针,因为0
是所谓的“空指针常量”的一个例子,它在标准中定义。然后比较两者。当且仅当my_char_ptr
是空指针时才进行比较,因为只有空指针与其他空指针进行比较等等。
my_char_ptr == (void*)0
将my_char_ptr
转换为void*
,然后将其与将0
转换为void*
(这是一个空指针)的结果进行比较。当且仅当my_char_ptr
是空指针时才进行比较,因为当您将指针转换为void*
时,结果是空指针,当且仅当源是空指针时。
空指针是否用0位表示的问题很有意思,但与代码分析无关。
认为NULL
是空指针(而不仅仅是空指针常量)的实际危险是您可能认为printf("%p", NULL)
已定义行为,或foo(NULL)
将调用void*
的{{1}}重载,而不是foo
重载,依此类推。
答案 2 :(得分:8)
否,因为他们在唯一可以保证作为示例工作的情况下使用它。 否则,是的。
虽然从某种程度上说你可能永远不会看到差异,严格来说,关注是正确的。
C ++标准要求(4.10):
0
的整数常量表达式,或类型为std::nullptr_t
的prvalue)转换为任何类型的空指针。这意味着,如果您对措辞很迂腐,那么void
和char
以及foo_bar
的空指针不仅不一定是零位模式,但也 不一定相同。只有相同类型的空指针必然是相同的(实际上,甚至不是这样,它只表示它们必须比较相等的,这不是同一个东西。)
事实上它明确地说“空指针值被转换为空指针值 目的地类型“表示这不仅是措辞的荒谬理论扭曲,而且确实是作为实施的合法特征。
这不管相同的文字0
将转换为每种类型的空指针这一事实。
在他们的示例中,它们与void*
进行了比较,由于上述转换规则,它们将起作用。此外,在实践中,每种类型的空指针都是您生活中可能遇到的每个架构上的零位模式(当然,这不是保证)。
答案 3 :(得分:7)
首先,即使在C ++中,我也不确定是否允许(charPtr == 0) != (charPtr == (void*)0)
。在这两种情况下,你都是
将空指针常量(0
)转换为指针
导致空指针。并且所有空指针都应该进行比较
等于。
其次,虽然我不知道你引用的段落的背景,
你真的不必担心NULL
是(void*)0
:
用户代码无法合法定义NULL
(至少不是如此)
包括任何标准头文件,以及C ++标准要求
NULL
被定义为空指针常量;即
常数积分表达式评估为0.(注意
尽管它的名称,空指针常量不能有一个指针
类型。)所以它可能是0
(或多或少的标准
定义,从C)的最开始,或可能0L
,
甚至(1-1)
,但不是((void*)0)
。 (当然,它可能会
也像__nullptr
这样的编译器内置常量
它的计算结果为整数0
,但如果不是则会触发警告
立即转换为空指针。
最后:没有要求空指针具有全部
0位,肯定有过这种情况
案子。另一方面,有一个要求
比较空指针和空指针常量将
评价为真;它取决于编译器使其工作。和
因为NULL
需要定义为空指针
常数,无论您使用NULL
还是0
,都只是一个问题
个人偏好和惯例。
编辑:
只是澄清一点:关键点涉及转换 一个“空指针常量”,一个整数常量表达式 评估为0.令人惊讶的是:
int zero = 0; // NOT a constant expression.
void* p1 = reinterpret_cast<void*>( zero );
void* p2 = 0;
if ( p1 == p2 ) // NOT guaranteed!
转换非常量表达式的结果 对指针求值为零不保证为空 指针。