【问题标题】:Can pointers to different types have different binary representations?指向不同类型的指针可以有不同的二进制表示吗?
【发布时间】:2021-02-08 12:53:59
【问题描述】:

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

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

对象指针可以显式转换为不同类型的对象指针。 当对象指针类型的prvaluev转换为对象指针类型“pointer to cv T”,结果是static_­cast<cv T*>(static_­cast<cv void*>(v))

[注 7:将指向 T1 类型对象的“指向T1”类型的指针转​​换为“指向T2”类型的指针(其中@987654335 @ 是一个对象类型并且T2 的对齐要求并不比T1 更严格)并且回到它的原始类型会产生原始指针值。 — 尾注]

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

【问题讨论】:

  • @JHBonarius 但是是什么迫使 C++ 实现使用相同的表示?在我的示例中,实现将指向int 的指针表示为右移两位的地址(您的“机器内存模型指针”)。如果它在指针被传递到某些与内存相关的机器代码指令之前将其移回,则不会有技术问题。 (当然,没有理智的实施可能会这样做,但这是关于这种可能性的语言律师问题。)
  • 如果我们有 4 字节大小/对齐的 int 和 8 字节大小/对齐的 long,是否可以将指针指向 int/long 表示为对象地址右移 2 /3 位,分别?这将有效地禁止将指针到长指针转换为指针到int你能解释一下吗?
  • 指向不同类型的指针可以有不同的二进制表示吗?“不同”是什么意思(即如何观察)? like this?
  • @LanguageLawyer 解释什么?你有什么特别不明白的地方?
  • @LanguageLawyer 至于你的第二个问题,基本上是的。但是我没有写任何关于观察的东西。我关心指针到不同类型的转换。 (例如,你能不能总是将一个指向T1的指针转换为指向T2的指针,然后再返回并确保它有效?看来这取决于T1T2的对齐要求。)

标签: c++ pointers language-lawyer bit-representation


【解决方案1】:

是的

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

void* 存储额外的偏移量,但 int* 不存储。将int* 转换为char* 有效(因为它必须符合标准),但char* 转换为int* 会丢失该偏移量(您的注释隐含允许)。

The Cray T90 supercomputer 就是这种硬件的一个例子。

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

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

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

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

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

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

然而,每个与对象无关的指针转换都需要通过void* 进行逻辑往返。

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

(来自 OP)

这包括void*T*;我还没有找到T*void* 的转换文本,以使其成为完整的 级答案。

【讨论】:

  • 那么“对象指针可以显式转换为不同类型的对象指针”呢?我也有同样的看法,只是不明白这个说法。
  • 根据标准,您可以转换 int * -> char * -> int * 并获得相同的地址,但您不能转换 char * -> int * -> @987654341 @ 并获得相同的地址,因为将丢失额外的字节寻址。这就是 Note 7 的内容。
  • @sklott:仅供参考,注释并没有说您不能进行后一种转换并获得相同的地址;它暗示(通过省略)您不能进行后一种转换并依赖于获取相同的地址(仅基于 C++ 标准)。
  • @daniel 我的非支持意见只是简单地描述了U* u=(U*)p_to_unrelated_other_type 的内容;它是cast-to-void-pointer(参见标准中的其他地方),然后是cast-void-to-target(参见标准中的其他地方)
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 1970-01-01
    • 2013-04-05
    • 2023-04-09
    相关资源
    最近更新 更多