类型转换四兄弟

发布时间:2026/7/6 14:38:00
类型转换四兄弟 类型转换四兄弟面试题为什么需要四种类型转换C 风格转型不够用吗C 风格转型(T)expr是 C 从 C 继承来的万能锤。问题是——它什么都敢转什么都不查而且不可搜索。// C 风格转型能干的事constchar*shello;int*p(int*)s;// 去掉 const 重新解释一把梭这段代码在 C 风格下合法编译但逻辑完全错误。更隐蔽的问题是当基类/派生类关系在重构中发生改变时C 风格转型不会报错而 C 转型会在编译期拦截不合理的组合。四种 C 转型的本质是把 C 风格的一把梭拆成四个独立工具各自只做一件事——让编译器帮你检查让搜索工具能找到你。Mermaid 图转型适用场景矩阵C 类型转换四兄弟static_cast 编译期检查dynamic_cast 运行时检查const_cast 去常转换reinterpret_cast 位模式重解相关类型转换int↔double, void*→T*父子类指针向上转型不检查运行时安全性需 RTTI 虚函数失败返回 nullptr / bad_cast遍历继承链查 typeinfo唯一去掉 const/volatile原对象非 const 则安全原对象是 const → UB不改二进制 只改类型char* 读 struct / 寄存器最危险 需手动保证对齐1. static_cast —— 编译期检查的温和派适用场景语义上相关的类型之间做转换。编译器在编译期检查类型是否沾亲带故不沾就报错。#includeiostreamclassBase{public:virtual~Base()default;};classDerived:publicBase{};intmain(){// 数值类型转换doubled3.14;intistatic_castint(d);// OK截断小数// void* 还原void*pvi;int*pistatic_castint*(pv);// OK// 向上转型隐式转换可替代Derived der;Base*pbstatic_castBase*(der);// 向下转型 —— 编译期通过但不检查运行时安全性Base b;Derived*pdstatic_castDerived*(b);// 编译通过运行时可能 UB// ↑ 这是 static_cast 的局限不检查实际对象类型}核心考点static_cast不生成运行时检查代码。向下转型时如果实际对象不是目标类型行为是未定义。面试官会追问——既然不安全为什么还在用答案性能优先场景下避免 RTTI 开销且程序员能通过设计保证转型安全。2. dynamic_cast —— 运行时检查的安全派需要 RTTI 支持——前提是类层次中有至少一个虚函数。转型时会遍历继承链查询目标对象的typeinfo判断是否可以安全转换。#includeiostream#includetypeinfoclassBase{public:virtual~Base()default;// 必须有虚函数};classDerived:publicBase{public:voidextra(){std::coutDerived::extra\n;}};intmain(){Base*b1newDerived();Base*b2newBase();// 指针版本 —— 失败返回 nullptrDerived*d1dynamic_castDerived*(b1);if(d1)d1-extra();// 输出 Derived::extraDerived*d2dynamic_castDerived*(b2);if(!d2)std::coutd2 转型失败\n;// 走进这里// 引用版本 —— 失败抛 bad_casttry{Derivedrddynamic_castDerived(*b2);}catch(conststd::bad_caste){std::coutbad_cast: e.what()\n;}deleteb1;deleteb2;}开销dynamic_cast的实现依赖__dynamic_castItanium ABI在继承链中做 strcmp 级别比较。简单单继承约 0.1–0.5μs复杂多重继承可能到微秒级。面试的高频追问是怎么优化 dynamic_cast——答案改用static_cast 枚举/虚函数接口约束。3. const_cast —— 唯一能去掉 const 的偏门C 里唯一一个可以移除const或volatile限制的转型。坑在于能不能修改取决于原对象本身是否 const不取决于你手上的指针类型。#includeiostreamvoidlegacy_api(char*str){str[0]H;// 可能 UB}intmain(){// 场景 1原对象非 const —— 安全charbuf[]hello world;legacy_api(const_castchar*(static_castconstchar*(buf)));// buf[0] 被改为 H这是合法的// 场景 2原对象是 const —— 未定义行为constintci42;intriconst_castint(ci);ri100;// UBci 真正是 conststd::coutci;// 可能输出 42编译器优化常量传播也可能 100}考察点面试官拿这个区分是否真正理解 const semantics。如果候选人回答const_cast 去掉后就能改说明只看了语法书。正确理解是——const_cast 只去掉类型系统里的 const 标签不影响对象本身的存储类型。真正只读区如.rodata上的 const 对象去掉后写入就是段错误。4. reinterpret_cast —— 最危险的位操作器不改变任何二进制位只改变编译器看待这些位的方式。看代码#includeiostream#includecstringstructPacket{uint32_tlen;uint8_tdata[4];};intmain(){// 场景 1从字节流读取结构体charraw[sizeof(Packet)]{};std::memcpy(raw,\x04\x00\x00\x00\x41\x42\x43\x44,8);Packet*pktreinterpret_castPacket*(raw);std::coutlenpkt-len\n;// 4小端// 场景 2指针转整数架构相关uintptr_t addrreinterpret_castuintptr_t(pkt);std::coutaddr0xstd::hexaddr\n;// 危险的错误用法floatf3.14f;int*ipreinterpret_castint*(f);// UB违反 strict aliasing// ↑ 编译器可能优化掉这个读取因为 float 和 int 不互为别名}reinterpret_cast 的危险清单风险说明对齐违规从非对齐地址读取多字节类型ARM 上直接 SIGBUSStrict Aliasing通过不同类型的指针读写同一内存编译器认为不可能发生优化后产生未定义行为类型擦除编译器不再知道内存里到底是什么类型move 语义、析构函数均不会触发可移植性字节序、指针大小、位域布局全部依赖平台C20 引入了std::bit_cast作为reinterpret_cast在平凡类型上的安全替代——它在编译期检查大小和对齐要求。面试追问什么时候用哪种场景正确选择错误选择int ↔ doublestatic_castreinterpret_cast语法不通void* 还原为原指针static_castreinterpret_cast能编译但不安全多态向下转型dynamic_caststatic_cast运行时 UB 不报调 C 风格 API 需要改 const 参数const_cast不用转型直接传编译报错从 char* 读取网络包reinterpret_caststd::memcpystatic_cast语法不通去掉 volatile 标记const_caststatic_cast语法不通一句话总结能static_cast就不用reinterpret_cast能用dynamic_cast就别用static_cast做向下转型const_cast只用在与非 const 遗产接口对接时reinterpret_cast是最后的手段——用它时必须确保对齐和别名规则。参考C 标准 [expr.static.cast]、Itanium C ABI — dynamic_cast、cppreference — const_cast