【问题标题】:Dereferencing the address of a pointer-casted variable取消引用指针转换变量的地址
【发布时间】:2019-05-10 09:43:33
【问题描述】:

我发现这段代码在一个更大的项目中被截断,这真的让我思考。

float sift_handler(int rs2, int rs4, int rs5);
int result1;


float temp = sift_hander(rs2, rs4, rs5);
result1 = *(int*)&temp;

据我了解,float temp 的地址被转换为 int 指针,然后被取消引用。当我编译这段代码时,我得到一个警告

warning: dereferencing type-punned pointer will break strict-aliasing rules

这样做的真正好处是什么? result1 = temp?另外,使用round() 比只进行投射更好吗?

【问题讨论】:

  • 它将给出该浮点数的二进制表示,而不是它所持有的值。它直接查看内存并将其解释为整数
  • 相关(C和C++):stackoverflow.com/q/346622/5470596
  • 违反严格的别名规则
  • @nwp 不是用任何一种语言取消引用result1 吗?
  • @Bathsheba 也是 C 中的 UB,至少基于 C18 标准草案(N2176),那里没有变化……

标签: c pointers


【解决方案1】:

这对 result1 = temp 的真正好处是什么?

它们是完全不同的操作。 result1 = temp 将值从浮点数转换为整数。如果temp1.0f,那么result1 将是1。如果默认舍入样式(即截断)不令人满意,您可以在赋值给整数之前使用round()

*(int*)&temp 另一方面重新解释 float 变量的位并将这些位存储到整数变量。 1.0f 可能会产生一些巨大的整数值。这称为类型双关

正如编译器告诉你的那样,使用无效类型的指针执行此取消引用是严格的别名冲突。进行这种双关语的正确方法是:

memcpy(&result, &temp, sizeof temp);

【讨论】:

  • 他想强制转换(获取整数部分)。 memcpy 无济于事
  • @P__J__ 在我看来,这个问题只是为了澄清一个混乱,而不是想要执行某些操作。
  • 这是我第一次听说类型双关语。我教它只是一种有趣的铸造方式。我会将其更改为memcpy,而不是因为违规不再存在。
  • @Mladia 如果您有很多旧代码,您可以使用 -fno-strict-aliasing 禁用严格别名(我假设您使用 gcc?)。它应该禁用别名优化并确保您当前的代码不会意外中断。
【解决方案2】:

访问浮点的表示曾经是一个老技巧。这意味着它不是强制转换,并且(如果没有被 UB 捕获)将给出不同的结果,但常见实现中的 0 除外。

至少从 C99 和 C++11 开始(对于以前的版本不确定),这样做会调用未定义行为,因为它违反了严格的别名规则。发明该规则是为了帮助编译器进行优化,声明一个变量只能通过它自己的类型或字符类型来访问。这样,当编译器在寄存器中存储了一个浮点数时,它可以假设这个浮点数不会因任何整数变化而改变(非常简单的解释)。

但由于它曾经在旧程序中大量使用,因此大多数编译器(甚至是最近的编译器)都可以选择忽略严格的别名规则。

在这里,您的编译器只是警告您此代码违反了严格的别名规则,并且可能会导致某些实现出现 UB。

TL/DR:使用类型转换的指针访问不同的类型是尝试重新解释底层表示,并且在 C 和 C++ 上都是 UB。绝对和演员表不一样。

【讨论】:

  • 有用的信息,谢谢。有人会尝试以某种方式摆脱警告还是就这样离开它? memcpy 是正确的方法吗(如另一个答案所述)?我正在阅读有关 memcpy 的信息并输入双关语,但这有点难以理解。
【解决方案3】:

这对 result1 = temp 的真正好处是什么

它做了一些完全不同的事情,它将浮点数转换为整数。所以根本不要使用指针双关语,直到你很好地理解了指针、数据类型、它在特定实现中的表示等等等

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-12
    • 1970-01-01
    • 1970-01-01
    • 2020-12-30
    • 2016-04-18
    • 1970-01-01
    • 2015-11-02
    • 2018-08-07
    相关资源
    最近更新 更多