对于
int *a, int *b
,a == b
是否意味着 (intptr_t)a == (intptr_t)b
?我知道在现代X86 CPU上是正确的,但C标准、POSIX或其他标准是否保证了这一点?int *a, int *b
,a == b
是否意味着 (intptr_t)a == (intptr_t)b
?我知道在现代X86 CPU上是正确的,但C标准、POSIX或其他标准是否保证了这一点?这并不由C标准保证。(本回答未涉及POSIX或其他标准中关于intptr_t
的说明。)C标准(2011年,草案N1570)对于intptr_t
的说明如下:
作为一个理论证明,一个反例是一个有24位地址的系统,其中高8位未使用,但可用的整数类型是8位、16位和32位。在这种情况下,C语言实现可以将7.20.1.4 1 下列类型指定了一种带有以下特性的有符号整数类型:任何有效的指向void的指针都可以转换为此类型,然后再转换回指向void的指针,结果将与原始指针比较相等:intptr_t
intptr_t
定义为32位整数,并通过将24位地址复制到32位整数中并忽略高8位来将指针转换为 intptr_t
。这些位可能是以前遗留下来的。当将 intptr_t
值转换回指针时,编译器会丢弃高8位,从而得到原始地址。在这个系统中,当对指针 a
和 b
进行指针比较 a == b
时,编译器只比较地址的24位。因此,如果 a
和 b
指向同一个对象,则表达式 a == b
将为真,但 (intptr_t) a == (intptr_t) b
可能由于被忽略的高位而导致求值为假。(注意,严格来说,a
和 b
应该是指向 void
的指针或在转换为 intptr_t
之前应该先转换为指向 void
的指针。)另一个例子是使用一些基址和偏移地址的系统。在这个系统中,指针可能由16位组成,其中前16位指定了某个基址,后16位指定了一个偏移量。基址可能是64字节的倍数,因此由 base 和 offset 表示的实际地址为 base•64 + offset。在这种系统中,如果指针 a
的基址为2,偏移为10,则表示与指针 b
基址为1,偏移为74的指针指向同一地址。比较指针时,编译器会计算每个指针的 base•64 + offset 并比较结果,因此,a == b
为 true。但是,当转换为 intptr_t
时,编译器可能只需复制位,从而为 (intptr_t) a
生成131,082(2•65536 + 10),为 (intptr_t) b
生成65,610(1•65536 + 74)。然后,(intptr_t) a == (intptr_t) b
为 false。但是将 intptr_t
转换回指针类型仍然保持原样,因为编译器将再次简单地复制位。
seg*16 + offset
,每种指针都有一个限制:所有标识同一对象部分的“大”指针必须指定相同的段(单个对象的大小限制为65,520字节),而所有“巨大”的指针必须在0-15范围内具有偏移量。尽管如此,如果您没有先我一步,您对于指针比uintptr_t
更窄的系统的示例与我的想法相符。 - supercat==
不能保证所有非NULL操作数的结果为真。指向不同对象的指针必须视为不同(除了指向数组结尾后一个位置的指针可能与指向其紧随其后的对象的指针相等)。 - Eric Postpischiluintptr_t
相同大小的“线性”指针的平台上,质量编译器将处理指针到 uintptr_t
的转换,以使相等的指针转换为相同的数值,并且如果给定 uintptr_t u;
,则如果 u==(uintptr)&someObject
,则可以使用 *(typeOfObject*)u
来访问 someObject
,至少在 someObject
的地址被转换为 uintptr_t
的时间和下一次通过其他方式访问 someObject
之间,而不考虑 u
如何获得其值。不幸的是,有些编译器太原始,无法识别将地址转换为 uintptr_t
将暗示从 uintptr_t
形成的指针可能能够识别相同的对象。u == (uintptr_t) &someObject
,那么 * (typeOfObject *) u
必然等于 &someObject
,因此可以使用它来访问 someObject
,这是由于往返转换规则的原因。(uintptr_t) &someObject
的结果是一个值,而不是 lvalue 或对象。如果 u
等于它,则 u
的值与该值相同——一个值仅由其自身特征化;它并不因为它是某个对象的值而有所区别。因此 (typeOfObject *) u
与 (typeOfObject *) (uintptr_t) &SomeObject
相同,并且往返转换规则表明它等于 &SomeObject
。 - Eric Postpischil((int volatile*)(uintptr_t)&y)[1]
,因为((int volatile*)(uintptr_t)&y)
是指向y
的指针(更糟糕的是,它是指向y
之前的数组中的最后一个元素的指针),所以表达式评估为y[1]
,即*(y+1)
,这是被禁止的评估。 - Eric Postpischilstruct s1 {int x1[2][1],y1;}; struct s2 { int x2[1][2],y2;}; union { struct s1 v1; struct s2 v2;} u;
,u.v1.y1
和u.v1.y2
是“同一对象”吗? - supercatintptr_t
必须产生一个等于原始指针的指针,这必然是指向同一个对象的指针(除了最后一个元素之外的问题)。 - Eric Postpischil
(int *)0x00010001 == (int *)0x00000011
将会是真的。 - Lee Daniel Crockeruintptr_t
,那是最近才有的东西。我们只有int
和long
。我不记得我的编译器如何处理远指针到长整型的转换。随着年龄的增长,记忆也在逐渐消逝... - Lee Daniel Crocker