#include <iostream>
int main(int argc, char * argv[])
{
int a = 0x3f800000;
std::cout << a << std::endl;
static_assert(sizeof(float) == sizeof(int), "Oops");
float f2 = *reinterpret_cast<float *>(&a);
std::cout << f2 << std::endl;
void * p = &a;
float * pf = static_cast<float *>(p);
float f3 = *pf;
std::cout << f3 << std::endl;
float f4 = *static_cast<float *>(static_cast<void *>(&a));
std::cout << f4 << std::endl;
}
我从我的可靠编译器中获取了以下信息:
me@Mint-VM ~/projects $ g++-5.3.0 -std=c++11 -o pun pun.cpp -fstrict-aliasing -Wall
pun.cpp: In function ‘int main(int, char**)’:
pun.cpp:11:45: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
float f2 = *reinterpret_cast<float *>(&a);
^
pun.cpp:21:61: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
float f4 = *static_cast<float *>(static_cast<void *>(&a));
^
me@Mint-VM ~/projects $ ./pun
1065353216
1
1
1
me@Mint-VM ~/projects $ g++-5.3.0 --version
g++-5.3.0 (GCC) 5.3.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
我不太明白为什么有些地方会出现类型转换错误,而有些地方却不会。
因此,严格别名:
严格别名是C(或C ++)编译器做出的一种假设,即对于不同类型对象的指针解除引用将永远不会引用相同的内存位置(即别名)。
第11行声称我正在破坏严格别名。我看不到这可能会产生任何伤害的情况——指针“出现”,立即被解除引用,然后被丢弃。很可能,这将编译成零条指令。这似乎完全没有风险——我正告诉编译器我想要什么。
第15-16行继续不引起警告,即使指向同一内存位置的指针现在已经留下来了。这似乎是gcc中的一个错误。
第21行引发了警告,这表明这不仅限于reinterpret_cast。
Union也不是更好的选择(强调我的):
……从联合体中读取最近未被写入的成员是未定义的行为。许多编译器实现了一种非标准语言扩展,能够读取联合体中非活动成员的值。
这个链接讨论了使用memcpy,但似乎只是隐藏了你真正想要完成的任务。
对于某些系统,将指针写入int寄存器或接收传入的字节流并将这些字节组装成float或其他非整数类型是必需的操作。
正确的、符合标准的方法是什么?
std::endl
。'\n'
用于结束一行。 - Pete Becker