【问题标题】:GCC implementation for behavior of reinterpret cast重新解释强制转换行为的 GCC 实现
【发布时间】:2011-12-10 19:57:18
【问题描述】:

我如何知道重新解释转换如何在 GCC 编译器上工作?文档中提到了吗?如果存在,我可以知道任何参考或链接吗?

【问题讨论】:

  • 我相信reinterpret_cast 的行为是由标准定义的,而不是由特定实现定义的。你到底想知道什么?
  • 它不是特定于编译器的,无论是GCC编译器还是VS编译器,它的工作方式都是一样的。它是由C++标准定义的,编译器不能改变它的行为。
  • 什么?标准中有很多实现定义的行为。 reinterpret_cast 是其中具有实现定义行为的功能之一。 标准显式允许实现定义其一些行为
  • 例如:如果我将指针转换为整数,在标准中会提到“reinterpret_cast 执行的映射是实现定义的”,所以我是否必须查看编译器如何执行映射?
  • 是的。 “定义的实现”具体意味着该标准没有指定应该发生什么,但符合标准的实现(编译器等)将选择特定行为并记录(定义)它

标签: c++ gcc reinterpret-cast


【解决方案1】:

阅读标准中的文档,它对不同类型非常明确。但是对于我们的基本指针:

指针可以显式转换为任何大到足以容纳它的整数类型。映射函数是实现定义的。 [ 注意:对于那些知道底层机器的寻址结构的人来说,这并不奇怪。 — 尾注 ] std::nullptr_t 类型的值可以转换为整数类型;该转换与将 (void*)0 转换为整数类型具有相同的含义和有效性。 [注意:不能使用 reinterpret_cast 将任何类型的值转换为 std::nullptr_t 类型。 ——尾注]

对于整数:

整数类型或枚举类型的值可以显式转换为指针。转换为足够大小的整数(如果实现中存在这样的整数)并返回相同指针类型的指针将具有其原始值;指针和整数之间的映射是由实现定义的。 [注意:除 3.7.4.3 中描述的情况外,这种转换的结果不会是安全派生的指针值。 ——尾注]

【讨论】:

  • @Loki.Astari:知道底层机器的寻址结构基本上是什么意思?
  • 假设您是一台普通机器,这意味着内存中的地址(它的措辞是这样的,因此没有内存的奇异机器(示例数据流)仍然与标准兼容。
【解决方案2】:

我在 g++ 中多次使用reinterpret_cast。在嵌入式编程中,将代表外设寄存器的struct 映射到其(固定)地址非常有用:

struct DEV_Registers
{
volatile uint32_t REGA;
volatile uint32_t REGB;
// ...
};

static DEV_Registers& DEV(*reinterpret_cast<DEV_Registers>(0x40000000));

这让我可以编写如下代码:

DEV.REGB = 0x12345678;

它做正确的事(将寄存器设置为 0x40000004 为值 0x12345678)并且非常清晰。

很难判断您的问题是否要求超出此范围的详细信息。

【讨论】:

  • 问题是要求 GCC 对此发表声明,而不是轶事 ;-) 但由于似乎不存在这样的声明 - 让我们假设 GCC 默默地遵循“意在不足为奇”条款通过简单地重新解释每个字节的“对象表示” - 你的轶事变得相当有用! Fwiw,我观察到同样的情况,例如关于reinterpret_casting [unsigned] char * 缓冲到SomeStruct (可能是风险最小的实现定义的转换,看到相反的定义)。但是,合并一个设置的地址是一个额外的技巧!我还没有那么低级 ;-)
  • 我希望我比轶事更好,但如果他们期望 GCC 开发人员适当的回应,他们问错地方了。一个更好的地方是GCC mail lists,尽管我无法从粗略的一瞥中看出哪一个最适合这个问题。是的,在低级世界中你必须做的一些邪恶的事情仍然比asm 块更好。
  • 不圣洁,或者只是难以置信有用!我使用了这么多神秘的小工具,以至于 C++ 是一个奇迹。如果我错了,请纠正我:我认为没有任何其他语言提供如此低级的可寻址性和高级语义。如果小心的话,这主要是可移植的——这正是因为我可以编写处理程序类/模板来保证位/字节顺序等。我很少会误入实现定义的行为的世界,但如果我这样做了,GCC 主要是好事直观地工作!无论是否具体记录... ;-)
  • 实际上,在 C++11(GCC 4.7 及更高版本)中,我可以做一些超级容易阅读的事情,并且仍然可以编译成与只使用 #defines 的代码相同或更小的代码.例如,我可以将驱动 LED 的 I/O 引脚定义为typedef OutputPin&lt;'B', 0, Initially::High, Active::Low&gt; LED0Pin;,它将 PIO 端口 B 位 0 描述为输出引脚,复位为高电平 (+VDD) 和低电平有效 (GND = LED 开启)。由于它是一个类型,它本身不消耗任何代码空间。
  • 然后代码用PIO::Setup&lt;LED0Pin&gt;(); 初始化引脚,它根据需要设置所有PIO 寄存器并驱动输出为高电平。要打开 LED,我只写 LED0Pin::Assert();,要关闭它,我只写 LED0Pin::Release();。如果稍后我发现 LED 实际上是高电平有效,我只需将 typedef 更改为 Initially::Low, Active::High,而不需要进行其他更改。由于内联和constexpr,所有这些“绒毛”都编译为单个操作。
猜你喜欢
  • 2014-11-29
  • 1970-01-01
  • 2016-07-27
  • 1970-01-01
  • 1970-01-01
  • 2020-09-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多