7

我想知道是否允许 C++ 实现以不同的方式表示指向不同类型的指针。例如,如果我们有 4-byte sized/alignedint和 8-byte sized/aligned long,是否可以将指向 / 的指针表示intlong分别右移 2/3 位的对象地址?这将有效地禁止将指向指针的指针转换为指向指针long的指针int

我问是因为[expr.reinterpret.cast/7]

对象指针可以显式转换为不同类型的对象指针。v对象指针类型的纯右值转换为对象指针类型“指向cv T的指针”时,结果为static_­cast<cv T*>(static_­cast<cv void*>(v)).

[注7:将指向类型T1对象的“pointer to”T1类型的指针转​​换为“pointer to T2”类型(其中T2是对象类型,对齐要求T2不比的更严格T1)并返回其原始type 产生原始指针值。——尾注]

第一句话表明我们可以将指针转换为任意两种对象类型。然而,(非规范性的)注释 7中的移情文本说,对齐在这里也起到了一定的作用。(这就是我想出这个的原因int——long上面的例子。)

4

2 回答 2

7

是的

作为一个具体的例子,有一个 C++ 实现,其中指向单字节元素的指针大于指向多字节元素的指针,因为硬件使用字(而不是字节)寻址。为了模拟字节指针,C++ 使用硬件指针加上额外的字节偏移。

void*存储额外的偏移量,但int*不存储。转换int*char*作品(因为它必须根据标准),但char*int*失去该偏移量(您的注释隐含允许)。

Cray T90 超级计算机就是这种硬件的一个例子。

我会看看我是否能找到标准论点,为什么这对于兼容的 C++ 编译器来说是有效的;我只知道有人这样做,并不是说这样做是合法的,而是该注释暗示它是合法的。

这些规则将在 to-from void 指针转换规则中。您引用的段落隐含地将转换的含义转发到那里。

7.6.1.9 静态转换 [expr.static.cast]

“指向 cv1 void 的指针”类型的纯右值可以转换为“指向 cv2 T 的指针”类型的纯右值,其中 T 是对象类型,而 cv2 与 cv1 具有相同的 cv 限定或大于 cv1 的 cv 限定。如果原始指针值表示内存中一个字节的地址A,并且A不满足T的对齐要求,则结果指针值未指定。否则,如果原始指针值指向对象 a,并且存在与 a 指针可互转换的类型为 T(忽略 cv 限定)的对象 b,则结果是指向 b 的指针。否则,指针值不会因转换而改变。

这表明转换为更对齐的类型会生成一个未指定的指针,但转换为实际上不存在的等于或更少对齐的类型不会更改指针值。

这是允许从指向 4 字节对齐数据的指针转换为指向 8 字节对齐数据的指针的强制转换导致垃圾。

然而,每个对象无关的指针转换都需要在逻辑上往返void*

对象指针可以显式转换为不同类型的对象指针。当对象指针类型的纯右值 v 转换为对象指针类型“指向 cv T 的指针”时,结果为static_­cast<cv T*>(static_­cast<cv void*>(v))

(来自OP)

涵盖void*T*; 我还没有找到T*转换void*文本来使它成为一个完整的级别的答案。

于 2021-02-08T13:32:03.633 回答
4

答案是肯定的。仅仅因为标准没有禁止它,实现可以决定对指向不同类型的指针有不同的表示,甚至对同一指针有不同的可能表示。

由于现在大多数架构都使用平面寻址(意味着指针的表示只是地址),因此没有充分的理由这样做。但我仍然记得旧的段:8086 系统的偏移地址表示,它曾经允许 16 位系统处理 20 位地址(1024k)。它使用 16 位段地址(移位 4 位以获得真实地址)和 16 位偏移量用于指针,或仅 16 位(相对于当前段)用于地址。在这种模式下,远指针有很多可能的表示。顺便说一句,远地址是大型和紧凑模式(ref )中的默认值(所以是由正常源产生的)。

于 2021-02-08T14:15:41.037 回答