我想知道是否允许C++实现以不同的方式表示指向不同类型的指针。例如,如果我们有4字节大小/对齐的int
和8字节大小/对准的long
,是否可以将指向-int
/long
的指针表示为分别右移2/3位的对象地址?这将有效地禁止将指向-long
的指针转换为指向-int
的指针。
我之所以提出这个问题,是因为[expr.relinterpret.cast/7]:
对象指针可以显式转换为不同类型的对象指针当对象指针类型的prvalue
v
转换为对象指针类型"pointer tocvT
"时,结果为static_cast<cv T*>(static_cast<cv void*>(v))
。[注意7:将指向类型为
T1
的对象的类型为"pointer toT1
"的指针转换为类型为"指针toT2
"(其中T2
是的对象类型,T2
的对齐要求不比T1
的对齐要求更严格)并返回到其原始类型会产生原始指针值。-结束注释]
第一句话建议我们可以将指针转换为任意两种对象类型。然而,(非规范性的)注释7中的移情文本表示,对齐在这里也起到了一定的作用。(这就是为什么我提出了上面的int
-long
示例。)
是的
作为一个具体的例子,有一个C++实现,其中指向单字节元素的指针大于指向多字节元素的指示器,因为硬件使用字(而不是字节)寻址。为了模拟字节指针,C++使用硬件指针加上额外的字节偏移量。
void*
存储该额外偏移,但int*
不存储。将int*
转换为char*
是可行的(在标准下必须如此),但char*
到int*
会丢失该偏移量(您的注释隐含地允许)。
克雷T90超级计算机就是这种硬件的一个例子。
我会看看我是否能找到标准的论点,为什么这对一个兼容的C++编译器来说是有效的;我只知道有人做了这件事,并不是说这样做是合法的,但那张纸条暗示着这是合法的。
这些规则将位于"到"from void指针强制转换规则中。你引用的这段话隐含地将转换的含义转移到了那里。
7.6.1.9静态铸造[expr.Static.cast]
类型为"pointer to cv1 void"的prvalue可以转换为类型为"pointer to cv2 T"的prvalue,其中T是对象类型,cv2与cv1具有相同的cv资格,或者比cv1具有更大的cv资质。如果原始指针值表示内存中字节的地址A,而A不满足T的对齐要求,则结果指针值未指定。否则,如果原始指针值指向对象a,并且存在类型为T(忽略cv限定)的对象b,该对象的指针可与a互换,则结果是指向b的指针。否则,指针值将因转换而保持不变。
这表明转换为更多对齐的类型会生成未指定的指针,但转换为实际上不存在的相等或更少对齐的类型不会更改指针值。
这是允许从指向4字节对齐数据的指针转换为指向8字节对齐数据指针的强制转换导致垃圾。
然而,每个与对象无关的指针投射都需要在逻辑上往返于void*
。
对象指针可以显式转换为不同类型的对象指针。当对象指针类型的prvalue v转换为对象指针类型"pointer to cv T"时,结果为
static_cast<cv T*>(static_cast<cv void*>(v))
。
(来自OP)
这涵盖了void*
到T*
;我还没有找到T*
到void*
的转换文本,使其成为一个完整的语言律师级别的答案。
答案是肯定的。仅仅因为标准没有禁止它,实现可以决定对不同类型的指针使用不同的表示,甚至对同一指针使用不同可能的表示。
由于大多数体系结构现在都使用平面寻址(意味着指针的表示只是地址),因此没有充分的理由这样做。但我仍然记得旧的部分:8086系统的偏移地址表示,它曾经允许16位系统处理20位地址(1024k)。它使用了一个16位的段地址(偏移4位以获得实际地址),远指针的偏移量为16位,或者近地址的偏移量仅为16位(相对于当前段)。在这种模式下,远指针有一堆可能的表示。顺便说一句,在大而紧凑模式(ref)中,远寻址是默认的(所以正常源产生的)。