【问题标题】:reinterpret_cast between ponter and different size intgeral typeponter 和不同大小的整数类型之间的 reinterpret_cast
【发布时间】:2014-07-03 10:39:58
【问题描述】:

当我们执行以下操作时实际会发生什么: 1)

int i = -1; // 32 bit
void *p;
p = reinterpret_cast<void*>(i) 

在 64 位架构上,sizeof(void*) == 8

2)

long long i; // 64 bit
void *p = (unsigned int)(-1);
i = retinterpret_cast<long long>(p) 

关于 32 位架构 sizeof(void*) = 4

我大致了解结果会是什么,但我希望有人根据 C++ 标准来描述机制以便更好地理解。

在第二种情况下,行为类似于“整体促销”(4.5)中描述的行为(我将是-1) 对于“int - unsigned long”的情况,那么我们通常说指针转换为有符号整数吗?

3)

int i = ...;
unsigned long long L = ...;
i = L;

这里适用什么规则?

【问题讨论】:

    标签: c++ casting implicit-conversion


    【解决方案1】:

    指针和整型之间的所有转换都是 实现定义。标准做出的唯一保证 是这样的:

    整数类型或枚举类型的值可以显式 转换为指针。转换为整数的指针 足够的大小(如果实现中存在这样的大小)和 回到同一个指针类型就会有它原来的值

    但是,该标准确实做出了声明(在 非规范性说明):

    它的目的是让那些知道 底层机器的寻址结构。

    从实施质量的角度来看,如果机器 具有线性寻址,将int 转换为指针应该 产生一个与int 的值相对应的值,如果 单词(无论大小)被视为一个整体类型;在 换句话说,位模式没有改变。如果积分 type 更小,并且是否定的,这是一个悬而未决的问题 它应该是符号扩展的,或者剩余的位是否应该 设置为0。我更喜欢第二个,但我认为两者都可以 被认为“不足为奇”。

    从务实的角度来看:有大量 那里的软件偶尔会投射一个小 void* 的非负整数值,并期望得到它 稍后带着演员回来。正式地,取回它需要 首先转换为intptr_t(或更大);否则代码 不应该编译。但我无法想象编译器会破坏它 除此以外。另一方面,对于负值,我觉得 明显不太确定。而且我不确定有多小, 任何一个。 (我目前在一种特殊情况下使用该技术 值小于 40 或 50。我与 MSC、g++ 和 Sun 合作 CC,至少,我无法想象它在其他任何一个上都失败了 我过去使用过的主流 Unix 机器。但我不会 依靠 16 位 Intel 或其他一些更奇特的 我见过的机器,当然不是在嵌入式系统上。)

    最后,至于您的确切问题:可能会有所不同。我会试试的 看看,指望不管它做什么,都有 某处的一些代码依靠它来做到这一点,并且 供应商不会冒险改变行为。形式上,因为它是 实现定义,实现者需要记录 它(当然,可以在下一个版本中更改它),但是 我通常发现它非常非常难以找到 文档。

    编辑:

    我刚刚注意到您的最后一个问题涉及unsigned long longint。这是一个积分转换,而不是 reinterpret_cast,因此适用不同的规则。或者更确切地说, 规则在不同的部分中指定:基本规则是 仍然是“定义的实现”:

    如果目标类型是有符号的,则值不变 可以在目标类型中表示(和位域 宽度);否则,该值是实现定义的。

    这里的 C 标准有些不同,因为它明确地 允许引发实现定义的信号。 (它的 有点夸张地说,C++ 中的“价值” 标准可能是提高信号,即使这将是 首选实现。)在实践中:所有 我知道的编译器只是忽略额外的高位。

    【讨论】:

    • 我是否理解正确,如果我在某个平台上测试(可能是负数) int 确实放置而没有更改指针的 4 个低字节(高位对我来说无关紧要)我可以期望它会在那里以这种方式工作,但在其他一些平台上它可能会失败?问题是我需要将 unsigned int 存储在指针中(通过 reinterpret_cast int -> ptr -> int)并稍后检索它。我们使用 Qt,当您创建 QModelIndex() 时,这是框架提供的存储 internalId 值(或仅使用指针)的唯一方法
    • @VladimirLenin 如果您使用某些编译器在某个平台上进行测试,可以。然而,发生的事情可能会从一个编译器更改为下一个编译器。一个可能更有趣的问题是你为什么想知道。如果目标是稍后提取int:首先转换为intptr_t,然后转换为指针,在提取时,首先转换为指向intptr_t的指针,然后转换为您想要的整数类型,应该可以正常工作无处不在,或者至少在任何地方都有线性寻址。
    【解决方案2】:

    行为未定义。任何事情都可能发生。甚至不能保证结果是一致的。 reinterpret_cast&lt;void*&gt;(1) == reinterpret_cast&lt;void*&gt;(1) 可能是false

    【讨论】:

    • 标准说实现已定义,这意味着它不是未定义的,事实上,标准必须记录它所做的事情(尽管有时很难找到这个文档)。
    • @JamesKanze The standard says implementation defined, which means that it's not undefined - 那是“未指定”,不是吗?
    • @ikh 该标准说“实现定义”。这意味着需要在标准给出的限制范围内定义和指定实现。它还保证以指针开头并使用足够大的整数类型时的往返。这里没有任何未定义的内容,甚至没有任何未指定的内容。
    • 我不同意“甚至不能保证结果是一致的 reinterpret_cast(1) == reinterpret_cast(1) 可能是假的”。如果是真的,那么在步骤(2)上将无法保证往返 [指针 1-> int 2-> 指针]
    • @VladimirLenin 实际上,在具有线性寻址的机器上,是的。理论上?可以肯定的是,reinterpret_cast&lt;void*&gt;(2) == reinterpret_cast&lt;void*&gt;(3);两个不同的int 绝对可以映射到同一个地址。
    猜你喜欢
    • 2012-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-14
    • 2020-09-17
    • 2015-10-21
    • 2019-03-24
    相关资源
    最近更新 更多