对于int *a, int *b
,a == b
是否暗示(intptr_t)a == (intptr_t)b
?我知道在现代X86 CPU上也是如此,但C标准或POSIX或任何其他标准是否为此提供了保证?
答案 0 :(得分:10)
C标准无法保证这一点。 (这个答案没有说明POSIX或其他标准是否说intptr_t
。)C标准(2011年,草案N1570)关于intptr_t
的说法是:
7.20.1.4 1以下类型指定一个带符号的整数类型,其属性是任何有效的void指针都可以转换为此类型,然后转换回指向void的指针,结果将等于原始指针:的使用intptr_t 强>
作为理论证明,一个反例是具有24位地址的系统,其中高8位未使用,但可用的整数类型是8位,16位和32位。在这种情况下,C实现可以使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
可能因为忽略的高位而评估为false。 (请注意,严格来说,a
和b
应指向void
,或者在转换为void
之前应转换为指向intptr_t
的指针。)< / p>
另一个例子是使用一些基址和偏移量寻址的系统。在该系统中,指针可能由指定某个基址的16位和指定偏移的16位组成。基数可能是64字节的倍数,因此 base 和 offset 表示的实际地址是 base •64 + offset < / em>的。在此系统中,如果指针a
具有基数2和偏移量10,则它表示与具有基数1和偏移量74的指针b
相同的地址。当比较指针时,编译器将评估 base <每个指针的/ em>•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
转换回指针类型的规则会使原始指针仍然成立,因为编译器只会再次复制这些位。
答案 1 :(得分:1)
标准不是试图指定普通平台上的质量实施应该维护的所有保证,而是寻求避免强制任何可能在任何可以想象的平台上保证昂贵或有问题的保证,除非它们非常有价值以证明任何平台的合理性。可能的成本。作者预计(当时合理地)可以提供基本上没有成本的更强保证的平台的质量编译器会这样做,因此需要明确强制编译器要做的事情。
如果看一下标准提供的实际保证,这是荒谬的懦弱。它指定将void *转换为uintptr_t然后再转换为void *将产生可与原始对象进行比较的指针,并且比较将报告两个指针相等。它没有说明如果代码使用往返转换指针执行任何其他操作会发生什么。一致的实现可以以忽略整数值的方式执行整数到指针的转换(除非它是空指针常量)并产生一些与任何有效或空指针不匹配的任意位模式,然后有它的指针只要操作数保持特殊位模式,-equality运算符就会报告“相等”。当然,质量实施不应该以这种方式表现,但标准中的任何内容都不会禁止它。
在没有优化的情况下,假设在使用与uintptr_t
大小相同的“线性”指针的任何平台上,质量编译器将处理指针转换为uintptr_t
是合理的。这样,等位指针的转换将产生相同的数值,而uintptr_t u;
给定u==(uintptr)&someObject
,那么*(typeOfObject*)u
可用于访问someObject
,至少在someObject
之间时间uintptr_t
的地址已转换为someObject
,下一次u
通过其他方式访问,而不考虑uintptr_t
如何保持其值。不幸的是,有些编译器过于原始,无法识别将地址转换为uintptr_t
会表明由import datetime
x = datetime.datetime.strptime("Mon Feb 15 2010", "%a %b %d %Y").strftime("%d/%m/%Y")
print(x)
构成的指针可能能够识别同一个对象。