必须启用-fno-exceptions:当项目无需throw/catch或运行环境不支持异常(如裸机嵌入式、rtos等)时,该参数可禁用异常代码生成,减小体积、提升启动速度、消除不确定开销。

什么时候该关掉C++异常机制
如果你的项目不需要 throw/catch,或者运行环境根本不支持异常(比如裸机嵌入式、实时操作系统、或某些静态链接约束场景),-fno-exceptions 就不是“可选优化”,而是必须启用的编译参数。它直接禁用 C++ 异常处理的代码生成,避免编译器插入栈展开(stack unwinding)相关逻辑和异常表(.eh_frame),从而减小二进制体积、提升启动速度、消除不确定的运行时开销。
关异常后哪些代码会出问题
启用 -fno-exceptions 后,所有显式使用异常的代码都会编译失败:
-
throw表达式变成编译错误 -
try/catch块被拒绝 - 带有
throw()或noexcept(false)说明符的函数声明仍合法,但实际抛出行为未定义(通常导致程序直接终止) -
std::vector::at()这类可能抛std::out_of_range的接口,在-fno-exceptions下仍会调用abort()而非抛异常——但前提是标准库本身也用相同参数编译;否则链接时可能失败
搭配 noexcept 和 -fno-rtti 才算完整关闭异常成本
单独加 -fno-exceptions 只禁用了异常逻辑,但没动 RTTI(运行时类型信息)。如果还用 dynamic_cast 或 typeid,就得连带加 -fno-rtti。更关键的是:很多 STL 容器(如 std::vector、std::string)在构造/赋值失败时默认依赖异常。要真正安全使用,得配合 noexcept 说明符约束接口,并确保所有第三方库也以 -fno-exceptions 编译——否则链接阶段可能报 undefined reference to __cxa_throw 这类符号缺失错误。
嵌入式和游戏引擎里为什么普遍用它
在资源受限或硬实时场景中,异常机制带来的不可预测性是致命的:
- 栈展开时间不可控,违反确定性响应要求
- 异常表增大 Flash 占用(对 Cortex-M 系列尤其敏感)
- 某些 ABI(如 ARM AAPCS)下,启用异常会强制保存更多寄存器,增加函数调用开销
- Unity、Unreal 引擎底层模块默认关闭异常,靠返回码或断言替代错误传播
真正容易被忽略的点是:即使你从不写 throw,只要链接了未用 -fno-exceptions 编译的标准库或第三方静态库,整个程序仍可能包含异常表和相关 runtime 支持——这会让 -fno-exceptions 失去意义。











