C++ 如何通过不同的类型重新解释数据?(键入双关语) #包括 int main(int argc,char*argv[]) { INTA=0x3f800000; std::cout
使用C++ 如何通过不同的类型重新解释数据?(键入双关语) #包括 int main(int argc,char*argv[]) { INTA=0x3f800000; std::cout,c++,strict-aliasing,type-punning,C++,Strict Aliasing,Type Punning,使用memcpy: 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]
memcpy
:
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.
如果您担心类型安全性和语义,可以轻松编写包装器:
memcpy(&f2, &a, sizeof(float));
如果您愿意,可以制作此包装器模板以满足您的需要。使用
memcpy
:
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.
如果您担心类型安全性和语义,可以轻松编写包装器:
memcpy(&f2, &a, sizeof(float));
如果您愿意,您可以制作此包装器模板以满足您的需要。请在此感谢Anton。他的答案是第一位的,他的答案是正确的 我发布这篇文章是因为我知道在你看到汇编程序之前你不会相信他: 鉴于:
void convert(float& x, int a) {
memcpy(&x, &a, sizeof(float));
}
使用-O2编译,下面是函数convert
的汇编程序输出,为清晰起见,添加了一些注释:
1065353216
1
为了进一步说明这一点,下面是使用-O2和-fomit帧指针编译的同一个函数:
#
# I'll give you £10 for every call to `memcpy` you can find...
#
__Z7converti: ## @_Z7converti
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
#
# here's the conversion - simply move the integer argument (edi)
# into the first float return register (xmm0)
#
movd %edi, %xmm0
popq %rbp
retq
.cfi_endproc
#
# did you see any memcpy's?
# nope, didn't think so.
#
请记住,此函数之所以存在,是因为我添加了该属性以防止编译器将其内联。实际上,启用优化后,整个函数将被优化。函数中的3行代码和调用站点上的调用将消失
现代优化编译器非常棒
但我真正想要的是这张
std::cout请归功于安东。他的答案是第一位的,他的答案是正确的
我发布这篇文章是因为我知道在你看到汇编程序之前你不会相信他:
鉴于:
void convert(float& x, int a) {
memcpy(&x, &a, sizeof(float));
}
使用-O2编译,下面是函数convert
的汇编程序输出,为清晰起见,添加了一些注释:
1065353216
1
为了进一步说明这一点,下面是使用-O2和-fomit帧指针编译的同一个函数:
#
# I'll give you £10 for every call to `memcpy` you can find...
#
__Z7converti: ## @_Z7converti
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
#
# here's the conversion - simply move the integer argument (edi)
# into the first float return register (xmm0)
#
movd %edi, %xmm0
popq %rbp
retq
.cfi_endproc
#
# did you see any memcpy's?
# nope, didn't think so.
#
请记住,此函数之所以存在,是因为我添加了该属性以防止编译器将其内联。实际上,启用优化后,整个函数将被优化。函数中的3行代码和调用站点上的调用将消失
现代优化编译器非常棒
但我真正想要的是这个std::cout正如你所发现的,reinterpret\u cast
不能用于类型双关
从C++20开始,您就可以通过
看
<>在其他编译器或旧的C++标准中,唯一可以做的是通过<代码> MycPy < /Calp> < /P>
然而,许多编译器有特定于实现的方式来进行类型双关,或者有特定于实现的关于类型双关的行为
ICC和Clang也支持该属性。如您所知,reinterpret\u cast
不能用于类型双关
从C++20开始,您就可以通过
看
<>在其他编译器或旧的C++标准中,唯一可以做的是通过<代码> MycPy < /Calp> < /P>
然而,许多编译器有特定于实现的方式来进行类型双关,或者有特定于实现的关于类型双关的行为
ICC和Clang也支持这一属性。参见Anton的回答绝对正确。这是唯一的便携方式,也是唯一保证工作的方式。甚至没有任何性能或内存开销,因为乐观主义者会看到你正在尝试做什么,很可能甚至不会复制任何内存。你必须这样做在您阅读和理解了几次标准之前,请信任我们。这不是问题,但不要使用std::endl
,除非您需要它所能提供的额外内容。'\n'
结束了一行。@RichardHodges没有不尊重的意思,但我不是“您必须在这方面信任我们”的粉丝-我没有该标准的副本,但我已经阅读了cppreference.com上提供的内容(不知道有多接近)。这一切告诉我的是,该标准中缺少一个应该解决的好答案。“但这似乎只是隐藏了你真正想要实现的目标。”是吗?你想实现的是把一种类型的指针当作另一种类型的指针,你想实现的是把int中的字节复制成float吗?在我看来,标准应该提供更好的支持。如果我memcpy,我就扔掉类型信息,准备调用正确地使用标准库函数。当重新解释转换的全部目的是“通过重新解释底层位模式在类型之间转换[]时,这似乎比重新解释转换风险更大安东的回答是绝对正确的。这是唯一的便携方式,也是唯一保证有效的方式。甚至没有任何性能或内存开销,因为乐观主义者会看到你正在尝试做什么,而且很可能,甚至不会复制任何内存。在你阅读并理解这些内容之前,你必须相信我们几次使用标准。这不是问题,但除非你需要额外的东西,否则不要使用标准。'\n'
结束一行。@RichardHodges没有不尊重的意思,但我不喜欢“你必须在这方面信任我们”—我没有标准的副本,但我已经阅读了cppreference.com上的内容(不知道有多接近)。这一切告诉我的是,标准中缺少一个好的答案,应该加以解决。“但这似乎只是隐藏了你真正想要实现的目标。”是吗?你想实现的是把一种类型的指针当作另一种类型的指针,你想实现的是把int中的字节复制成float吗?在我看来,标准应该提供更好的支持。如果我memcpy,我就扔掉类型信息,准备调用正确使用标准库函数。这似乎风险更大
union Float
{
float __attribute__((__may_alias__)) f;
uint32_t __attribute__((__may_alias__)) u;
};
uint32_t getFloatBits(float v)
{
Float F;
F.f = v;
return F.u;
}