持续更新中: 现代C++高效编程实战手册:从项目痛点直通现代C++精髓
动手学习CUDA编程
3年C++开发还看不懂Workflow框架?这套教程让你逆袭
动手学习100个Qt项目
读懂OpenCV的每一行代码
![]()
你写了一行 throw std::runtime_error("something went wrong") 。抛出、接住,程序继续跑。干净利落。
但"抛出"和"接住"之间,隔着一套你在源码里完全看不到的机制。编译器在你写的每个函数旁边偷偷生成了一张表,记着:如果异常从这个位置飞出去,哪些局部对象需要析构、哪个 PC 范围有 catch 块、怎么把寄存器恢复到上一帧的状态。这张表在不抛异常时确实零开销——没有一条额外指令被执行,没有一个寄存器被多占,甚至连一次条件判断都不会多做。但一旦你 throw 了,运行时库要在这张表里做一次线性扫描,逐帧回溯调用栈,每一帧都要解码一段 DWARF 字节码来恢复寄存器状态。调用栈越深,开销越大。
三句话概括: 编译时付账,运行时不抛免费,抛了很贵。
这篇文章要做的事很具体: 从 GCC 的源码出发,追踪一次 throw 从触发到被 catch 的完整路径,搞清楚编译器生成了什么表、运行时怎么查这张表、每一步的性能开销从哪来。 不泛泛讲"异常处理是什么"。逐函数、逐数据结构地追踪 libstdc++ 的 eh_throw.cc 、 eh_personality.cc 和 libgcc 的 unwind-dw2.c 、 unwind.inc ,让你知道那些"隐藏的表"长什么样、"O(n) 栈展开"具体在做什么。
主要参考 GCC trunk 最新源码,涉及的关键文件:
libstdc++-v3/libsupc++/eh_throw.cc:__cxa_throw的实现libstdc++-v3/libsupc++/eh_personality.cc:__gxx_personality_v0人格函数libgcc/unwind.inc:_Unwind_RaiseException的两阶段展开libgcc/unwind-dw2.c:DWARF 帧状态机和 CFA 程序解释器
先从最直接的问题开始:你写的那行 throw ,编译器到底把它变成了什么?
一、throw 变成了什么:从一行 C++ 到三个函数调用
你写的代码可能是这样的:
voidprocess(conststd::string& input){特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.