假设我有一个float
,我希望将其解释为unsigned int
以便进行一些比喻(现在我们假设为sizeof(float) == sizeof(int)
)。现在有各种“工作”方式,例如:
float f = 0.5f;
unsigned int u;
u = * (unsigned int*) (&var); /* works in most compilers */
union {
float f;
unsigned int u;
} uconv;
uconv.f = f;
u = uconv.u; /* works in most compilers */
现在我不明白标准,但IIRC第一个是未定义的行为,这要归功于严格的别名规则,我听说后者也是未定义的行为。
那么将一种类型的值解释为另一种类型的正确定义方法是什么?
请添加您的答案是否适用于C89,C90,C99甚至C11。
答案 0 :(得分:2)
通常,正确的方法是将变量的地址强制转换为void*
,char*
或unsigned char*
,因为任何其他指针强制转换都可能导致未定义的行为(实际上,因为{{ 3}})。 int*
和float*
是不兼容的指针类型。
当sizeof
两种类型相同时,以下内容将起作用:
memcpy(&u, &var, sizeof(int)); // note: implicit conversion to void [const] *
您获得的值显然是实现定义的,因为它取决于int
和float
在内存中的表示方式。
严格别名一直在strict aliasing。
编辑:我以前认为导致UB的union
黑客错误。 C11草案的相关部分指出:
如果用于读取union对象内容的成员与上次使用的成员不同 在对象中存储一个值,该值的对象表示的相应部分被重新解释 作为6.2.6中描述的新类型中的对象表示(有时称为''类型的过程) 双关语””)。
- Foonote 95至6.5.2.3结构和工会成员
答案 1 :(得分:1)
您的第一种方法不仅因为混叠而存在危险,而且还因为对齐。 float
和int
可能具有不同的对齐属性。
union方法很好,但理论上甚至unsigned int
都可以有陷阱表示。 (但仅限于古怪的架构。)
唯一保证没有陷阱表示的类型是unsigned char
。标准的方式是通过union
和unsigned char[sizeof(float)]
或者使用memcpy
将对象复制到这样的数组上来实现您想要实现的目标。
答案 2 :(得分:0)
这是一个非常好的问题。联合技巧是我测试的唯一一个适用于所有编译器和所有警告级别的技巧。如果你想支持不同的endianesses,可能unsigned char [4]优于unsigned int ...
正如larsmans写的那样,没有任何100%可移植的方法可以做你想要的,因为底层的float实现没有被任何C / C ++标准定义。