# CERT C++ 规范


# CERT Cpp 规范

> [https://ww2.mathworks.cn/help/bugfinder/cert-c-rules.html?lang=en](https://ww2.mathworks.cn/help/bugfinder/cert-c-rules.html?lang=en)

**[English Version](./005_CertCpp规范英文版.md)**

----

## 声明与初始化

[DCL30-C] 为对象声明恰当的存储期。

[DCL39-C] 避免结构体填充带来的信息泄露。

[DCL40-C] 不要为同一个函数或对象创建不兼容的声明。

[DCL50-CPP] 不要定义 `C` 风格的可变参数函数。

[DCL51-CPP] 不要声明或定义保留标识符。

[DCL52-CPP] 切勿用 `const` 或 `volatile` 限定引用类型。

[DCL53-CPP] 不要编写存在语法歧义的声明。

[DCL54-CPP] 在同一作用域内成对重载内存分配与释放函数。

[DCL55-CPP] 跨信任边界传递类对象时，避免信息泄露。

[DCL56-CPP] 避免静态对象初始化过程中的循环依赖。

[DCL57-CPP] 不要让异常从析构函数或内存释放函数中逃逸。

[DCL58-CPP] 不要修改标准命名空间。

[DCL59-CPP] 不要在头文件中定义未命名命名空间。

[DCL60-CPP] 遵守单一定义规则。

----

## 表达式

[EXP34-C] 不要解引用空指针。

[EXP35-C] 不要修改具有临时生存期的对象。

[EXP36-C] 不要将指针强制转换为对齐要求更严格的指针类型。

[EXP37-C] 调用函数时传入正确数量和类型的参数。

[EXP39-C] 不要通过类型不兼容的指针访问变量。

[EXP42-C] 不要对填充数据进行比较操作。

[EXP45-C] 不要在选择语句中执行赋值操作。

[EXP46-C] 不要对类布尔操作数使用位运算符。

[EXP47-C] 调用 `va_arg` 时不要传入类型不正确的参数。

[EXP50-CPP] 不要依赖带副作用操作数的求值顺序。

[EXP51-CPP] 不要通过类型不正确的指针删除数组。

[EXP52-CPP] 不要依赖未求值操作数中的副作用。

[EXP53-CPP] 不要读取未初始化的内存。

[EXP54-CPP] 不要在对象的生存期之外访问对象。

[EXP55-CPP] 不要通过非 `cv` 限定类型访问 `cv` 限定的对象。

[EXP56-CPP] 不要调用语言链接不匹配的函数。

[EXP57-CPP] 不要对指向不完整类的指针进行强制转换或删除操作。

[EXP58-CPP] 为 `va_start` 传入正确类型的对象。

[EXP59-CPP] 对合法的类型和成员使用 `offsetof()` 宏。

[EXP60-CPP] 不要跨执行边界传递非标准布局类型的对象。

[EXP61-CPP] lambda 对象的生存期不得超过其任何引用捕获的对象。

[EXP62-CPP] 不要访问对象表示中不属于其值表示的二进制位。

[EXP63-CPP] 不要依赖被移动后对象的值。

----

## 整数

[INT30-C] 确保无符号整数运算不会发生回绕。

[INT31-C] 确保整数转换不会导致数据丢失或被错误解读。

[INT32-C] 确保有符号整数运算不会发生溢出。

[INT33-C] 确保除法和取余运算不会出现除零错误。

[INT34-C] 不要对表达式执行负数位数的移位，或移位位数大于等于操作数本身的位宽。

[INT35-C] 使用正确的整数精度。

[INT36-C] 谨慎执行指针与整数之间的相互转换。

[INT50-CPP] 不要强制转换为取值范围外的枚举值。

----

## 容器

[ARR30-C] 不要生成或使用越界的指针或数组下标。

[ARR37-C] 不要对指向非数组对象的指针进行整数加减操作。

[ARR38-C] 确保库函数不会生成无效指针。

[ARR39-C] 不要对指针执行缩放后的整数加减操作。

[CTR50-CPP] 确保容器的下标和迭代器处于合法范围内。

[CTR51-CPP] 使用合法的引用、指针和迭代器来访问容器元素。

[CTR52-CPP] 确保库函数不会发生溢出。

[CTR53-CPP] 使用合法的迭代器区间。

[CTR54-CPP] 不要对不属于同一容器的迭代器执行减法操作。

[CTR55-CPP] 对迭代器使用加法运算符时，确保结果不会溢出。

[CTR56-CPP] 不要对多态对象使用指针算术运算。

[CTR57-CPP] 提供合法的排序谓词。

[CTR58-CPP] 谓词函数对象不应是可修改的。

----

## 字符与字符串

[STR30-C] 不要尝试修改字符串字面量。

[STR31-C] 确保字符串的存储空间足以容纳字符数据和空终止符。

[STR32-C] 不要向期望字符串的库函数传入非空终止的字符序列。

[STR34-C] 字符转换为更大的整数类型前，先强制转换为 `unsigned char` 类型。

[STR37-C] 字符处理函数的参数必须能表示为 `unsigned char` 类型。

[STR38-C] 不要混淆窄字符、宽字符字符串及其对应的处理函数。

[STR50-CPP] 确保字符串的存储空间足以容纳字符数据和空终止符。

[STR51-CPP] 不要尝试通过空指针构造 `std::string` 对象。

[STR52-CPP] 使用合法的引用、指针和迭代器来访问 `basic_string` 的元素。

[STR53-CPP] 对元素访问执行范围检查。

----

## 内存管理

[MEM30-C] 不要访问已释放的内存。

[MEM31-C] 动态分配的内存不再使用时应及时释放。

[MEM34-C] 仅释放动态分配的内存。

[MEM35-C] 为对象分配足够的内存。

[MEM36-C] 不要通过调用 `realloc()` 修改对象的对齐方式。

[MEM50-CPP] 不要访问已释放的内存。

[MEM51-CPP] 正确释放动态分配的资源。

[MEM52-CPP] 检测并处理内存分配错误。

[MEM53-CPP] 手动管理对象生存期时，显式执行对象的构造与析构。

[MEM54-CPP] 为 `placement new` 提供对齐正确、存储空间充足的指针。

[MEM55-CPP] 遵守动态存储管理函数替换的相关要求。

[MEM56-CPP] 不要将已被持有所有权的指针值存入无关的智能指针中。

[MEM57-CPP] 避免对过对齐类型使用默认的 `operator new`。

----

## 输入与输出

[FIO30-C] 禁止用户输入进入格式字符串。

[FIO32-C] 不要对设备执行仅适用于文件的操作。

[FIO34-C] 区分从文件读取的字符与 `EOF`、`WEOF`。

[FIO37-C] 不要假定 `fgets()` 或 `fgetws()` 调用成功时一定会返回非空字符串。

[FIO38-C] 不要复制 `FILE` 对象。

[FIO39-C] 对流交替执行输入输出操作时，中间必须执行刷新或定位操作。

[FIO40-C] `fgets()` 或 fgetws() 调用失败时重置字符串。

[FIO41-C] 调用 `getc()`、`putc()`、`getwc()`、`putwc()` 时，流参数不要带有副作用。

[FIO42-C] 文件不再使用时应及时关闭。

[FIO44-C] `fsetpos()` 仅使用 `fgetpos()` 返回的合法值。

[FIO45-C] 访问文件时避免 `TOCTOU`（检查时间与使用时间）竞态条件。

[FIO46-C] 不要访问已关闭的文件。

[FIO47-C] 使用合法的格式字符串。

[FIO50-CPP] 对文件流交替执行输入输出操作时，中间必须执行定位操作。

[FIO51-CPP] 文件不再使用时应及时关闭。

----

## 异常与错误处理

[ERR30-C] 调用会设置 `errno` 的库函数前，先将 `errno` 置为 `0`；且仅当函数返回值表明调用失败后，再检查 `errno` 的值。

[ERR32-C] 不要依赖 `errno` 的不确定值。

[ERR33-C] 检测并处理标准库的错误。

[ERR34-C] 字符串转换为数值时检测并处理错误。

[ERR50-CPP] 不要让程序异常终止。

[ERR51-CPP] 处理所有异常。

[ERR52-CPP] 不要使用 `setjmp()` 或 `longjmp()` 函数。

[ERR53-CPP] 不要在构造函数或析构函数的函数级 `try` 块处理程序中，引用基类或类的数据成员。

[ERR54-CPP] 异常捕获处理程序应按参数类型从派生程度最高到最低的顺序排列。

[ERR55-CPP] 遵守异常规范。

[ERR56-CPP] 确保异常安全。

[ERR57-CPP] 异常处理过程中不要发生资源泄漏。

[ERR58-CPP] 处理 `main()` 函数开始执行前抛出的所有异常。

[ERR59-CPP] 不要跨执行边界抛出异常。

[ERR60-CPP] 异常对象必须具备不抛出异常的拷贝构造能力。

[ERR61-CPP] 通过左值引用捕获异常。

[ERR62-CPP] 字符串转换为数值时检测并处理错误。

----

## 面向对象编程

[OOP50-CPP] 不要在构造函数或析构函数中调用虚函数。

[OOP51-CPP] 不要对派生类对象进行切片操作。

[OOP52-CPP] 删除多态对象时，其基类必须具备虚析构函数。

[OOP53-CPP] 按规范顺序编写构造函数的成员初始化列表。

[OOP54-CPP] 妥善处理自拷贝赋值操作。

[OOP55-CPP] 不要使用指向成员的指针运算符访问不存在的成员。

[OOP56-CPP] 遵守替换处理程序的相关要求。

[OOP57-CPP] 优先使用特殊成员函数和重载运算符，而非 `C` 标准库函数。

[OOP58-CPP] 拷贝操作不得修改源对象。

----

## 并发

[CON33-C] 使用库函数时避免竞态条件。

[CON37-C] 多线程程序中不要调用 `signal()` 函数。

[CON40-C] 不要在一个表达式中两次引用同一个原子变量。

[CON41-C] 将可能虚假失败的函数封装在循环中。

[CON43-C] 多线程代码中禁止出现数据竞争。

[CON50-CPP] 不要销毁处于锁定状态的互斥量。

[CON51-CPP] 确保异常发生时主动持有的锁能被正确释放。

[CON52-CPP] 多线程访问位域时防止数据竞争。

[CON53-CPP] 按预定义顺序加锁以避免死锁。

[CON54-CPP] 将可能虚假唤醒的函数封装在循环中。

[CON55-CPP] 使用条件变量时，保证线程安全与活性。

[CON56-CPP] 不要对调用线程已持有的非递归互斥量执行投机性加锁。

----

## 其他规则

[ENV30-C] 不要修改特定函数返回值所引用的对象。

[ENV31-C] 执行可能使环境指针失效的操作后，不要依赖该环境指针。

[ENV32-C] 所有退出处理程序必须正常返回。

[ENV33-C] 不要调用 `system()` 函数。

[ENV34-C] 不要存储特定函数返回的指针。

[FLP30-C] 不要使用浮点变量作为循环计数器。

[FLP32-C] 预防并检测数学函数中的定义域和值域错误。

[FLP34-C] 确保浮点转换的结果在目标类型的取值范围内。

[FLP36-C] 整数值转换为浮点类型时保留精度。

[FLP37-C] 不要通过对象表示来比较浮点值。

[MSC30-C] 不要使用 `rand()` 函数生成伪随机数。

[MSC32-C] 为伪随机数生成器设置正确的种子。

[MSC33-C] 不要向 `asctime()` 函数传入非法数据。

[MSC37-C] 确保控制流永远不会到达非 `void` 函数的末尾。

[MSC38-C] 若预定义标识符可能仅以宏的形式实现，不要将其当作对象处理。

[MSC39-C] 不要对值不确定的 `va_list` 调用 `va_arg()`。

[MSC40-C] 不要违反语言约束。

[MSC41-C] 切勿硬编码敏感信息。

[MSC50-CPP] 不要使用 `std::rand()` 生成伪随机数。

[MSC51-CPP] 确保随机数生成器被正确设置种子。

[MSC52-CPP] 有返回值的函数必须在所有退出路径都返回值。

[MSC53-CPP] 不要从声明为 `[[noreturn]]` 的函数中返回。

[MSC54-CPP] 信号处理函数必须是普通函数。

[PRE30-C] 不要通过拼接生成通用字符名。

[PRE31-C] 避免不安全宏的参数带有副作用。

[PRE32-C] 不要在调用类函数宏时使用预处理指令。

[SIG31-C] 不要在信号处理函数中访问共享对象。

[SIG34-C] 不要在可中断的信号处理函数内部调用 `signal()`。

[SIG35-C] 不要从计算异常的信号处理函数中返回。

----

**[English Version](./005_CertCpp规范英文版.md)**

----

