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;
}