0

0

c++的类型双关(Type Punning)有哪些安全和危险的做法? (std::bit_cast vs union)

冰火之心

冰火之心

发布时间:2026-01-11 09:40:37

|

785人浏览过

|

来源于php中文网

原创

std::bit_cast是C++20起唯一标准定义安全的类型双关方式,要求源目标类型大小相等、trivially_copyable,仅比特复制;union读写在C++17前为UB,C++20限制仍严;reinterpret_cast和void*中转均不安全;memcpy是C++20前最可靠替代。

c++的类型双关(type punning)有哪些安全和危险的做法? (std::bit_cast vs union)

std::bit_cast 是目前最安全的类型双关方式

从 C++20 开始,std::bit_cast 是唯一被标准明确定义为“合法且无未定义行为”的类型双关机制。它要求源和目标类型大小严格相等、均为 trivially_copyable,且不涉及指针/引用重解释——只做比特位复制。

常见误用点:传入 std::vector 或含 padding 的 struct 会编译失败;对 floatuint32_t 这类固定尺寸转换最稳妥。

float f = 3.14f;
uint32_t bits = std::bit_cast(f); // ✅ 安全、明确、可优化
// uint32_t bits = *reinterpret_cast(&f); // ❌ 可能触发 strict aliasing 优化错误
  • 编译期检查尺寸和可复制性,出错即报红,不靠运行时行为赌运气
  • 生成的汇编通常就是一条 mov(无内存读写),性能不打折扣
  • 不能用于跨平台浮点布局假设(如 IEEE 754 非强制,但实际几乎全部满足)

union 成员读取是未定义行为,除非满足窄条件

在 C++17 及之前,通过 union 写入一个成员后读取另一个成员(即使大小相同)属于未定义行为(UB)。C++20 引入了“活跃成员”例外:若两个成员共用同一段内存且无非静态数据成员(即 plain old data),且读取的是“可表示为字节序列”的类型,则允许——但该规则极其脆弱,且主流编译器(GCC/Clang)并未完全按此实现。

典型翻车场景:union { int i; float f; } u; 先赋值 u.i = 0x3f800000,再读 u.f —— 看似合理,但可能被优化掉、或在 -O2 下产生意外结果。

立即学习C++免费学习笔记(深入)”;

雪鸮AI
雪鸮AI

高效便捷的智能绘图辅助工具,一键生成高质量效果图。

下载
  • 即使编译通过,-fstrict-aliasing(默认开启)会让编译器假定不同类型的指针不重叠,从而删除看似“冗余”的读取
  • Clang 的 -Wunsafe-buffer-usage 和 GCC 的 -fsanitize=undefined 可能不捕获 union 类型双关
  • 仅当 union 所有成员都是标准布局、无构造函数/析构函数、且你**完全控制内存对齐与填充**时才勉强可控

reinterpret_cast + memcpy 是兼容性最强的“手动 bit_cast”

当无法使用 C++20(如嵌入式环境或旧工具链),memcpy 绕过类型系统是最广泛认可的安全方案。它不触发别名规则,因为 char* 是唯一被允许别名其他类型的指针类型。

float f = -1.0f;
uint32_t bits;
static_assert(sizeof(f) == sizeof(bits));
memcpy(&bits, &f, sizeof(bits)); // ✅ 明确、可移植、无 UB
  • std::bit_cast 多一次函数调用开销,但现代编译器基本内联为 mov 指令
  • 必须手动校验 sizeof 相等,否则 memcpy 会越界或截断(例如 doubleuint32_t
  • 不能用于非 trivially_copyable 类型(如含虚函数、引用成员的 class)

哪些做法绝对要避免

以下操作在任何标准版本、任何主流编译器下都不可靠:

  • 直接 reinterpret_cast(f) —— 严格别名违规,-O2 下可能返回垃圾值或 0
  • 通过 void* 中转再 cast:*(uint32_t*)static_cast(&f) —— 本质仍是 reinterpret_cast,不改变语义
  • std::memcpy 拷贝到非 trivial 类型对象(如 std::string)—— 即使大小匹配,也会破坏内部状态
  • 依赖 __attribute__((packed))#pragma pack 强制 union 对齐后读取——padding 行为仍由 ABI 决定,不可跨平台保证

类型双关真正的复杂点不在语法,而在于你是否清楚自己正在绕过编译器的类型安全假设。哪怕 std::bit_cast 正确使用,也要确认两端类型的二进制表示在目标平台上确实是互操作的——比如 int32_tfloat 的字节序一致,且浮点格式是预期的 IEEE 754。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

556

2024.04.28

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

98

2025.10.23

c语言union的用法
c语言union的用法

c语言union的用法是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型,union的使用可以帮助我们节省内存空间,并且可以方便地在不同的数据类型之间进行转换。使用union时需要注意对应的成员是有效的,并且只能同时访问一个成员。本专题为大家提供union相关的文章、下载、课程内容,供大家免费下载体验。

122

2023.09.27

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

533

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

51

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

194

2025.08.29

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

78

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 4.4万人学习

Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号