是否将两个 void 指针与 C++ 中定义的不同对象进行比较?

Posted

技术标签:

【中文标题】是否将两个 void 指针与 C++ 中定义的不同对象进行比较?【英文标题】:Is comparing two void pointers to different objects defined in C++? 【发布时间】:2012-01-03 18:14:23 【问题描述】:

灵感来自this answer 关于动态转换为void*

...
bool eqdc(B* b1, B *b2) 
    return dynamic_cast<void*>(b1) == dynamic_cast<void*>(b2);

...
int main() 
    DD *dd = new DD();
    D1 *d1 = dynamic_cast<D1*>(dd);
    D2 *d2 = dynamic_cast<D2*>(dd);
    ... eqdc(d1, d2) ...

我想知道在 C++ 中是否完全定义了行为(根据 03 或 11 标准)比较两个指向 有效的(不)相等的 void 指针 >,但不同的对象

更一般地说,但可能不那么相关,是比较(==!=)始终定义的void*类型的两个值,还是要求它们持有指向有效对象/内存区域的指针?

【问题讨论】:

【参考方案1】:

C++11、5.10/1:

可以比较相同类型的指针(指针转换后) 为了平等。当且仅当相同类型的两个指针比较相等 如果它们都为空,则都指向同一个函数,或者两者都指向 代表同一个地址

所以是的,具体对比一下就OK了。

一般来说,尝试创建一个不是有效地址的指针值是未定义的行为 - 例如,使用指针算法在数组的开头或结尾后一个 - 更不用说使用它们。像(void*)23 这样的东西的结果是实现定义的,因此除非实现的特定许可,否则实际上比较它们也是未定义的行为,因为实现可能会定义结果是void* 的陷阱值。

【讨论】:

为什么实现定义在这里暗示未定义? @Kos:嗯,这个值是实现定义的,这意味着实现可以定义它返回一个陷阱值(如果实现有指针的陷阱值),当用过的。因此,除非您在编写代码时知道实现定义的行为是什么,或者至少知道实现基本上将指针值视为永远不会陷阱的整数,否则它就像是 UB 一样糟糕。大多数实现都是安全的,但因为问题是关于标准保证什么......【参考方案2】:

C 说:

两个指针比较相等当且仅当两者都是空指针,两者都是指向 相同的对象(包括指向对象的指针和开头的子对象)或函数, 两者都是指向同一数组对象的最后一个元素的指针,或者一个是指针 一个指向一个数组对象的末尾,另一个是指向另一个数组对象开头的指针 恰好紧跟在地址中的第一个数组对象之后的数组对象 空间。

C++ 说:

两个相同类型的指针如果比较相等 并且只有当它们都为空,都指向同一个函数,或者都代表同一个地址时。

因此这意味着:

一)

在 C++(根据 03 或 11 标准)中,比较两个指向有效但不同对象的 void 指针的(不)相等性是完全定义的行为。

是的,在 C 和 C++ 中都是如此。您可以比较它们,在这种情况下,如果它们指向同一个对象,它们应该比较为真。这很简单。

b)

是在比较(==或!=)两个始终定义的 void* 类型的值,还是要求它们持有指向有效对象/内存区域的指针?

同样,比较是明确定义的(标准说“当且仅当”,所以两个指针的每次比较都是明确定义的)。但是后来……

C++ 以“地址”的形式进行讨论,所以我认为这意味着标准要求它“按我们的预期”工作, 但是,C 要求两个指针都为空,或者指向对象或函数,或者指向数组对象之后的一个元素。如果我的阅读技能没有关闭,这意味着如果在给定平台上您有两个具有相同值的指针,但未指向有效对象(例如未对齐),则比较它们应该是明确定义的并产生错误。

这太令人惊讶了!

确实是not how GCC works:

int main() 
    void* a = (void*)1; // misaligned, can't point to a valid object
    void* b = a;
    printf((a == b) ? "equal" : "not equal");
    return 0;

结果:

equal

也许 C 中的 UB 有一个不是空指针并且不指向对象、子对象或数组中最后一个对象的指针?嗯...这是我的猜测,但后来我们得到了:

整数可以转换为任意指针类型。除先前指定的外,该 结果是实现定义的,可能没有正确对齐,可能不指向 引用类型的实体,可能是陷阱表示。

所以我只能解释为上面的程序是定义良好的,C标准期望它打印“不等于”,而GCC并没有真正遵守标准,而是给出了更直观的结果。

【讨论】:

“未对齐,无法指向有效对象”为假,因为理论上它可以指向char 对象。实际上它并没有指向一个对象,至少不是在用户模式下的(比如说)linux上,但我认为我们不应该阅读C中的那句话,说指针的==运算符应该以某种方式检测地址当前是否有效。相反,我认为不言而喻(事实上,在其他地方已经说过)有效程序首先不会使用无效地址。 如果标准禁止它生成无效指针,一切都很好......但看起来它没有(c99 6.3.2.3 p5)。好吧,我们都明白“应该说”什么(并且确实在 C++ 中已经说过),但可能是 C99 在这里不够严格......?

以上是关于是否将两个 void 指针与 C++ 中定义的不同对象进行比较?的主要内容,如果未能解决你的问题,请参考以下文章

UB 是不是将 void 指针与 C 中的类型化指针进行比较(是不是相等)?

[转载]C++中引用与指针的区别(详细介绍)

nullptr和NULL

C++编程知识:什么是万能指针?详解C语言万能指针的妙用

C语言 NULL赋结构体指针变量

一文搞懂C++引用与指针