
避免 STL 容器动态内存分配
嵌入式系统通常没有完整堆管理或禁用 malloc/free,而标准 STL 容器(如 std::vector、std::string)默认在堆上分配内存,运行时可能崩溃或不可预测。
- 改用固定大小的栈数组或预分配缓冲区,例如用
std::array替代std::vector - 若必须用动态容器,选用无堆依赖的替代库(如
etl或folly::small_vector的裁剪版),并确保其配置为使用静态内存池 - 禁用异常和 RTTI 后,部分 STL 实现仍会隐式调用
operator new——需检查编译器链接的 libstdc++/libc++ 是否启用了-fno-use-cxa-atexit和自定义new操作符
慎用虚函数与运行时多态
虚函数表(vtable)增加 ROM 占用,虚调用引入间接跳转开销,在 Cortex-M0/M3 等无分支预测的小核上延迟明显;且每个含虚函数的类实例额外携带 4 字节 vptr。
- 优先用模板 + CRTP(
Curiously Recurring Template Pattern)实现编译期多态 - 若必须运行时分发,考虑函数指针表或状态机枚举,而非继承+虚函数
- 确认编译器是否内联了简单虚函数调用(
g++ -O2可能优化掉单实现类的虚调用,但不可依赖)
控制编译器生成代码体积与延迟
默认 C++ 编译选项常为通用场景优化,嵌入式需主动约束:避免模板爆炸、抑制冗余符号、关闭非必要 ABI 特性。
- 添加
-fno-exceptions -fno-rtti -fno-threadsafe-statics,防止隐式插入异常处理桩和 guard 变量逻辑 - 对时间敏感函数加
__attribute__((optimize("O3,fast-math,no-tree-vectorize")))(注意fast-math可能破坏 IEEE 浮点语义) - 用
arm-none-eabi-g++ -ffunction-sections -fdata-sections配合链接脚本--gc-sections删除未引用代码/数据 - 检查
sizeof和alignof—— 某些 STL 类型(如std::function)在不同工具链下尺寸差异极大,可能意外撑爆栈
栈空间必须显式估算并监控
嵌入式通常只配几 KB 栈(如 2KB),而 C++ 默认栈行为比 C 更“激进”:临时对象、隐式拷贝、异常栈展开都会快速耗尽空间,且多数 MCU 无栈溢出检测。
立即学习“C++免费学习笔记(深入)”;
- 禁止递归调用,所有函数调用深度应静态可分析(可用
arm-none-eabi-gcc -fstack-usage生成 .su 文件) - 避免大尺寸局部对象:
std::array直接压栈,等价于裸数组但更易被忽略 - 重载全局
operator new和operator delete为 panic-on-fail,而非返回 nullptr;同时禁用 placement new 以外的所有 new 形式 - 启动时用 MPU 或软件标记栈末尾(如填魔数),运行中定期校验——很多硬故障实际是静默栈溢出覆盖了相邻变量
extern "C" void* operator new(size_t size) {
while(1); // 或触发 HardFault
}
extern "C" void operator delete(void*) noexcept {}
C++ 在资源受限环境不是不能用,而是每个语法特性都要问一句:它悄悄申请了什么内存?增加了多少指令周期?有没有替代的、更透明的写法?最危险的从来不是写不出功能,而是没意识到某行看似干净的 std::string s = "hello" 正在往只剩 64 字节的栈里塞 24 字节对象加堆分配。









