这种严格的别名如何定义未定义的行为?

时间:2017-07-06 06:49:06

标签: c union strict-aliasing

我知道,我已经看到了,但我找不到任何好的解释为什么这个未定义的行为:

#include <stdio.h>
#include <stdint.h>

//Common union for both types
union float_int {
    float f;
    uint32_t i;
};

int main(void) {
    union float_int fi;
    //This should be problematic
    uint32_t* i_ptr = (uint32_t *)&fi.f;

    fi.f = 10.0f;
    printf("%f : %u\r\n", fi.f, fi.i); //Prints: 10.000000 : 1092616192 which is OK
    printf("%u\r\n", *i_ptr); //Prints: 1092616192 which is also OK

    return 0;
}

如果我们检查内存表示,两者都是4-bytes长,所以在指向或类似的情况下没有内存溢出

这种未定义的行为如何?

int main() {
    union float_int fi;
    void* v_ptr = &fi.f;
    uint32_t* i_ptr = (uint32_t *)v_ptr;
}

此代码仍然是未定义的行为吗?我想将float号码视为unsigned integer 32-bits

为什么使用memcpy是唯一可行的方式?

2 个答案:

答案 0 :(得分:1)

这不是严格的别名,而是严格别名的违规

首先,你正在做

 uint32_t* i_ptr = (uint32_t *)&fi.f;   //converting to a non-character type pointer

然后,您尝试通过

访问它
  printf("%u\r\n", *i_ptr);   //access value via incompatible lvalue expr.

导致问题。 floatuint32_t 不是兼容类型。

引用C11,章节§6.5/ P7

  

对象的存储值只能由具有其中一个的左值表达式访问   以下类型: 88)

     

- 与对象的有效类型兼容的类型,

     

- 与对象的有效类型兼容的类型的限定版本

     

- 对应于有效类型的有符号或无符号类型   对象,

     

- 对应于合格版本的有符号或无符号类型的类型   有效的对象类型,

     

- 包含其中一种上述类型的聚合或联合类型   成员(包括,递归地,子集合或包含的联合的成员),或

     

- 字符类型。

在回复评论时,让我们看看C11,章节§6.2.6.1

  

存储在任何其他对象类型的非位字段对象中的值由n × CHAR_BIT组成   bits,其中n是该类型对象的大小,以字节为单位。该值可以复制到   类型unsigned char [n]的对象(例如,memcpy);得到的字节集是   称为值的对象表示。

  

某些对象表示不需要表示对象类型的值。如果存储   对象的值具有这样的表示,并由左值表达式读取   没有字符类型,行为是未定义的。 [...]这种表述被称为   陷阱表示。

答案 1 :(得分:0)

浮点数和整数表示形式彼此不同。 这就是为什么通过定义这样一个联合,不是一个好的用例。

在您的示例中,执行从void *到uint32_t *的转换。 转换是在指针级别完成的。 这意味着i_ptr指向内存中的一个位置,被视为一个整数,而不是自己更改位。

总而言之,如果您希望此转换工作,您需要修改变量的内部表示。例如:

UIImage