MISRA C++不是写法优化指南,而是通过编译期约束物理剔除未定义行为的工程协议:禁用new/delete消除堆不确定性,强制栈分配与显式作用域保障资源生命周期可预测,要求整数运算全程可控、分支全覆盖、static_assert覆盖所有编译期契约。

直接说结论:不是“加几个检查”或“套个模板”就能合规,而是要从变量声明、整数运算、资源管理到控制流全部重置默认思维——MISRA C++ 不是写法优化指南,它是用编译期约束把未定义行为(UB)从代码里物理剔除的工程协议。
怎么禁用 new/delete 且不破坏 RAII
静态分配不是唯一解,关键在于让资源生命周期完全可预测。MISRA C++ 禁用 new/delete 的真实意图是消除堆不确定性,不是反对面向对象。
- 用栈对象 + 显式作用域控制资源生命周期:
std::array<uint8_t, 256> buffer;比std::vector<uint8_t>更安全,因为后者底层仍可能触发动态分配(即使没显式调用new) - 若必须复用内存,用预分配池 + placement
new(需在偏离流程中批准),但禁止delete;析构函数仍可调用,只要不释放堆内存 -
std::unique_ptr和std::shared_ptr默认禁用——它们隐含delete调用;可用自定义 deleter 空实现,但必须通过静态分析工具验证无实际释放逻辑
整数溢出怎么防才不算“打补丁”
MISRA C++ 不接受运行时检查兜底(如 if (a > UINT8_MAX - b)),它要求所有算术表达式在编译期就能证明不会溢出。
- 优先用固定宽度类型:
uint8_t、int16_t,避免int这类平台相关类型参与计算 - 所有二元运算前做显式截断或提升:
uint16_t result = static_cast<uint16_t>(a) + static_cast<uint16_t>(b);——不是为了“防止溢出”,而是让溢出行为变成明确定义的模运算(C++ 标准保证无符号溢出是模运算) - 禁用隐式提升:
uint8_t a = 255; int b = a + 1;违反 Rule 10.1,因为a被隐式转为int,而int宽度不可控;应写作uint8_t b = static_cast<uint8_t>(a + 1U);
为什么 if/else 必须带 else 且不能只写 ;
这不是风格问题,是为防止控制流跳变导致状态机错乱。MISRA C++ 要求每个分支都显式处理,哪怕只是空语句。
立即学习“C++免费学习笔记(深入)”;
-
if (flag) { do_something(); }违反 Rule 15.6 —— 编译器无法静态确认flag == false时程序行为是否安全 - 正确写法:
if (flag) { do_something(); } else { /* no-op */ }或更推荐if (flag) { do_something(); } else { handle_unexpected_case(); } - 空
else块必须写成完整语句块(即{}),不能简写为;,否则静态分析工具会报 Rule 15.7(空语句不构成有效控制流终点)
static_assert 怎么用才真正起效
多数人只用它校验常量,但 MISRA C++ 要求它覆盖所有“编译期可判定的契约”。漏掉一个,就等于留了一个静态分析无法捕获的漏洞口。
- 不只是类型大小:
static_assert(sizeof(MyStruct) == 32, "MyStruct must be exactly 32 bytes"); - 还要覆盖值域约束:
static_assert(MAX_RETRY_COUNT - 枚举值必须显式指定底层类型并校验:
enum class State : uint8_t { IDLE = 0, RUNNING = 1, ERROR = 255 }; static_assert(static_cast<uint8_t>(State::ERROR) == 255, "State::ERROR must fit in uint8_t");
最易被忽略的一点:MISRA C++ 规则之间有强依赖。比如禁用 dynamic_cast(Rule 5.2.10)和强制初始化所有变量(Rule 9.3)共同作用,才能保证 static_cast 在多态场景下不越界——单独做任一者都没用。合规不是打勾清单,是规则网络的协同生效。


















