可以表达"(ptr == 0)!=(ptr ==(void *)0)"真的是真的吗?

时间:2014-04-08 12:24:25

标签: c++ pointers

我在与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");
}

这就是我理解这个论坛帖子的主张。

4 个答案:

答案 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 == 00转换为my_char_ptr的类型。这会产生一个空指针,因为0是所谓的“空指针常量”的一个例子,它在标准中定义。然后比较两者。当且仅当my_char_ptr是空指针时才进行比较,因为只有空指针与其他空指针进行比较等等。

my_char_ptr == (void*)0my_char_ptr转换为void*,然后将其与将0转换为void*(这是一个空指针)的结果进行比较。当且仅当my_char_ptr是空指针时才进行比较,因为当您将指针转换为void*时,结果是空指针,当且仅当源是空指针时。

空指针是否用0位表示的问题很有意思,但与代码分析无关。

认为NULL是空指针(而不仅仅是空指针常量)的实际危险是您可能认为printf("%p", NULL)已定义行为,或foo(NULL)将调用void*的{​​{1}}重载,而不是foo重载,依此类推。

答案 2 :(得分:8)

,因为他们在唯一可以保证作为示例工作的情况下使用它。 否则,是的。

虽然从某种程度上说你可能永远不会看到差异,严格来说,关注是正确的。

C ++标准要求(4.10):

  1. 空指针常量(可以是求值为0的整数常量表达式,或类型为std::nullptr_t的prvalue)转换为任何类型的空指针。
  2. 两个相同类型的空指针 比较相等。
  3. 可以将类型指针指向cv-T的prvalue转换为指向-cv-void的指针,并且相应地调整空指针值。
  4. 派生类的指针可以转换为基类的指针,并且相应地调整空指针值。
  5. 这意味着,如果您对措辞很迂腐,那么voidchar以及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!

转换非常量表达式的结果 对指针求值为零保证为空 指针。