两个指针比较相等转换为整数类型比较相等吗?

Are two pointers comparing equal converted to an integer type compare equal?

提问人:Some Name 提问时间:5/21/2019 更新时间:5/21/2019 访问量:337

问:

问题:如果指针比较相等,它们的整数转换值是否也相等?

例如:

void *ptr1 = //...
void *ptr2 = //...
printf("%d", ptr1 == ptr2); //prints 1

这是否意味着这也是?(intptr_t) ptr1 == (intptr_t) ptr21

从务实的角度来看,这应该是对的。但考虑到标准在以下方面的规定:7.20.1.4(p1)

以下类型指定具有以下属性的有符号整数类型 任何有效的指针都可以转换为此类型,则 转换回 的指针,结果将相等 到原始指针:voidvoid

    intptr_t

这与实现可以将相同的指针转换为不同的值(取决于一些奇怪的情况)并不矛盾,从而保留了转换回来的值会产生相同的指针。

所以,我认为不,比较相等的指针的整数转换值不一定彼此相等。

c 指针 language-lawyer

评论

2赞 yhyrcanus 5/21/2019
我无法想象以相同的方式两次铸造一个值会产生不同的结果。但我不能说我在任何地方都有证据。
3赞 Rorschach 5/21/2019
这似乎是编译器会忽略的明显无操作,而只是猜测
0赞 Some Name 5/21/2019
@yhyrcanus我进行了一些实验,整数确实具有相同的值,但我想确定它是否符合计数。
4赞 Lee Daniel Crocker 5/21/2019
在像 Intel 80286 这样的分段内存架构中,指向同一对象的两个指针可能具有不同的值。

答:

8赞 StoryTeller - Unslander Monica 5/21/2019 #1

你的分析是正确的。除了允许在§6.3.2.3中与整数进行转换外,该标准没有提到该转换应如何进行。诚然,上有一个“往返”要求,但这并不妨碍可能超过一次旅行,编译器会根据某些约束或要求选择一个或另一个。intptr_t

因此,事实上,C 标准不需要成立。(intptr_t) ptr1 == (intptr_t) ptr2

评论

1赞 Some Name 5/21/2019
我想我们甚至不能暗示,即使那样,尽管 null 指针常量的定义是具有值的整数常量表达式。(intptr_t) ptr == 0ptr == NULL0
2赞 StoryTeller - Unslander Monica 5/21/2019
@SomeName - 是的。
0赞 Some Name 5/21/2019
与这个问题无关,但无论如何,我认为有必要在转换为之前显式转换任何不同于 to 的指针类型,不是吗?void *void *intptr_t
1赞 John Bollinger 5/21/2019
@SomeName,这取决于你所说的“必要”是什么意思。具体来说,往返保证适用。将其他指针类型转换为具有实现定义的行为,甚至可能是未定义的行为,但它们是允许的,因此从这个意义上说,不需要转换为 first。然而,在实践中,作为实现质量问题,您通常可以依靠往返任何对象指针类型。void *intptr_tvoid *intptr_t
0赞 Some Name 5/21/2019
@JohnBollinger我的意思是,如果实现支持而不是往返指向对象类型的指针是可能的,因为没有任何额外的实现定义的假设。intptr_tobj_t * --> void * --> intptr_t --> void * --> obj_t *
6赞 supercat 5/21/2019 #2

指针大小介于两种整数类型之间的实现(例如,分段模式 80386,指针为 48 位)可能会处理如下操作:

uintptr_t my_uintptr = (uintptr_t)myptr;

通过存储到前 48 位并保留其余位保留任意值,前提是以后的转换忽略这些位的值。myptrmy_uintptrmyptr = (void*)my_uintptr;

由于不能保证同一指针的重复转换将产生相同的值,因此,尽管通过不同的方式生成,但同样不能保证被转换的指针比较相等。uintptr_t

但是,如果实现记录了指针和整数的存储格式,并记录了转换的执行方式,并且如果行为无法以与该文档一致的方式运行,而不维护更强的语义保证,则应期望实现支持此类保证。我不认为该标准要求实现以与其文档一致的方式作为一致性条件,但是质量实现应该按照文档的形式运行的概念应该是不言而喻的,以至于标准不需要要求它。

评论

2赞 Ben Zotto 5/21/2019
是一个很好的非折磨的例子,说明这种假设可能会失败。
0赞 supercat 5/21/2019
@BenZotto:假设失败的硬件平台很少见。描述事物如何存储的实现,但随后以与此不一致的方式运行,在实践中是一个更大的问题。就我个人而言,我认为任何真正努力实现高质量的人都会寻求避免这种不一致,而不考虑标准是否禁止它们,但并不是每个人都有这种感觉。
0赞 Ben Zotto 5/21/2019
哦,绝对地,我 100% 同意你的看法。我很欣赏你在回答中也提出了这一点。但我被这句话感动了,因为在阅读这个问题时,这似乎是一根非常愚蠢的语言律师的头发,而你巧妙地提供了一个理论陷阱的真实例子。(当然,在实践中,实现几乎肯定必须特意做到如此古怪!
2赞 supercat 5/21/2019
@BenZotto:我不知道是否有任何使用 48 位指针的 80386 编译器支持 64 位整数类型,或者它们是否根本没有定义 uintptr_t,因为当 C99 添加 64 位类型时,x86 上的几乎所有内容都使用“平面”32 位模式,所有内容都使用单段。此外,我认为该模式的高质量编译器应该提供一个选项来显式清除uintptr_t的上位,而不考虑标准是否要求它,即使它还提供了一个选项来保持这些位不确定。
0赞 supercat 5/25/2021
@AlexShpilkin:该标准并不“禁止”任何此类事情,除非在严格符合的程序中。是否在不可移植程序中支持此类结构的问题被留作其管辖范围之外的“实现质量”问题。不幸的是,clang 和 gcc 的作者拒绝承认该标准并没有试图禁止低质量的实现,因此一致性本身并不是质量的衡量标准。
7赞 Keith Thompson 5/21/2019 #3

在几乎所有的实现中,当且仅当两个指针的表示相等时,两个指针才相等,但标准并不能保证这一点。

事实并不意味着这一点,并且具有相同的表示形式。N1570 6.5.9 第6段:ptr1 == ptr2ptr1ptr2

当且仅当两个指针都是空指针时,两个指针的比较相等,两者 是指向同一对象的指针(包括指向对象的指针和 子对象)或函数,两者都是指向一个的指针 过去了同一数组对象的最后一个元素,或者一个是指向 一个超过一个数组对象的末尾,另一个是指向 碰巧紧随其后的不同数组对象的开始 地址空间中的第一个数组对象。

例如,假设指针表示为一个由两部分组成的实体,第一部分标识内存段,第二部分标识该段内的字节偏移量。如果两个段可以重叠,则同一内存地址可以有两个不同的指针表示形式。这两个指针的比较是相等的(生成的代码可能需要做一些额外的工作才能实现这一点),但如果转换为仅复制表示,则 .intptr_t(intptr_t)ptr1 != (intptr_t)ptr2

(指针到整数的转换也有可能使表示形式规范化。

这种可能性就是为什么并且对于指向不同对象的指针进行了很好的定义,但关系运算符 (, , , ) 未定义。相等运算符必须确定两个指针是否指向同一位置,但关系运算符只能比较偏移量而忽略基部分(假设每个对象都在单个段中)。在实践中,几乎所有现代系统都具有单体地址空间,并且相等和关系运算符始终如一地工作,即使标准不要求它们这样做。==!=<<=>>=

评论

0赞 S.S. Anne 8/25/2019
“或者一个是指向一个数组对象末尾的指针,另一个是指向另一个数组对象的开头的指针,该数组对象恰好紧跟在地址空间中的第一个数组对象之后”——哇,这是合法的吗?这是非常令人惊讶的。
2赞 Keith Thompson 8/25/2019
@JL2210:这是合法的,因为不允许它是不切实际的。给定 ,如果碰巧在内存中紧随其后,则 和 p1' 将指向相同的内存位置。使产量成为虚假的结果将需要不合理的额外工作。int x; int y; int *p0 = &x + 1; int *p1 = &y; if (p0 == p1) ...yxp0p0 == p1