重磅!《安全规则集合》正式发布 https://mp.weixin.qq.com/s/_AX2dOte7QKMYQg3UjN4bQ
safe-rules/c-cpp-rules.md at main · Qihoo360/safe-rules https://github.com/Qihoo360/safe-rules/blob/main/c-cpp-rules.md
C/C++安全规则集合 
Bjarne Stroustrup: “C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.”
针对C、C++语言,本文收录了409种需要重点关注的问题,可为制定编程规范提供依据,也可为代码审计以及相关培训提供指导意见,适用于桌面、服务端以及嵌入式等软件系统。
每个问题对应一条规则,每条规则可直接作为规范条款或审计检查点,本文是适用于不同应用场景的规则集合,读者可根据自身需求从中选取某个子集作为规范或审计依据,从而提高软件产品的安全性。
规则说明
规则按如下主题分为15个类别:
- Security:敏感数据保护、攻击防御等问题
- Resource:资源分配、使用与回收
- Precompile:预处理指令、宏、注释等问题
- Global:全局及命名空间作用域相关问题
- Type:类型相关的设计与实现
- Declaration:声明
- Exception:异常
- Function:函数实现
- Control:流程控制
- Expression:表达式
- Literal:常量
- Cast:类型转换
- Buffer:缓冲区
- Pointer:指针
- Style:样式、风格等问题
每条规则包括:
- 编号:规则在本文中的章节编号,以“R”开头,称为Section-ID
- 名称:用简练的短语描述违反规则的状况,以“ID_”开头,称为Fault-ID
- 标题:规则的定义
- 说明:规则设立的原因、示例、违反规则的后果、改进建议、参照依据、参考资料等内容
当代码出现违反规则的情况时,可分为:
- Error:可直接导致错误的问题
- Warning:可导致错误或存在隐患的问题
- Suspicious:代码的可疑形式
- Suggestion:对提高代码质量的建议
规则的说明包含:
- 示例:规则相关的示例代码,指明符合规则(Compliant)的和违反规则(Non-compliant)的情况
- 相关:与当前规则有相关性的规则,可作为扩展阅读的线索
- 依据:规则依照的ISO/IEC标准,C规则以ISO/IEC 9899:2011为主,C++规则以ISO/IEC 14882:2011为主
- 配置:某些规则的对象可由用户指定,审计工具可以此为参照实现定制化功能
- 参考:规则参考的其他规范条款,如C++ Core Guidelines、MISRA、CWE、SEI CERT等,也可作为扩展阅读的线索
规则的相关性分为:
- 特化:设规则A的特殊情况需要由规则B阐明,称规则B是规则A的特化
- 泛化:与特化相反,称规则A是规则B的泛化
- 相交:设两个规则针对不同的问题,但在内容上有一定的交集,称这两个规则相交
规则以“标准名称:版本 章节编号(段落编号)-性质”的格式引用标准,如“ISO/IEC 14882:2011 5.6(4)-undefined”,表示引用C++11标准的第5章第6节第4段说明的具有undefined性质的问题。
其中“性质”分为:
- undefined:一般指某种错误,使程序产生undefined behavior
- unspecified:标准不作明确规定的情况,由编译器或环境自主定义,具有随意性
- implementation defined:由实现定义,也是由编译器或环境自主定义,与unspecified不同,要求有明确的文档支持
- deprecated:已过时或已废弃的用法
本文以ISO/IEC 9899:2011以及ISO/IEC 14882:2011为主要依据,兼顾C18、C++17以及历史标准,没有特殊说明的规则同时适用于C语言和C++语言,只适用于某一种语言的规则会另有说明。
规则选取
本文是适用于不同应用场景的规则集合,读者可选取适合自己需求的规则。
指出某种错误的规则,如有“不可”、“不应”等字样的规则应尽量被选取,有“禁用”等字样的规则可能只适用于某一场景,可酌情选取。
如果将本文作为培训内容,为了全面理解各种场景下存在的问题,应选取全部规则。
规则列表
1. Security
- R1.1 敏感数据不应以明文形式写入代码
- R1.2 敏感数据不可被系统外界感知
- R1.3 敏感数据在使用后应被有效清理
- R1.4 公共成员或全局对象不应记录敏感数据
- R1.5 预判由用户输入造成的不良后果
- R1.6 避免在一个事务中通过路径多次访问同一文件
- R1.7 访问共享数据应遵循合理的同步机制
- R1.8 对文件设定合理的权限
- R1.9 对用户设置合理的权限
- R1.10 不应引用危险符号名称
- R1.11 避免调用具有危险性的函数
- R1.12 不应调用已过时的函数
- R1.13 禁用不安全的字符串函数
- R1.14 确保字符串以空字符结尾
- R1.15 避免使用由实现定义的库函数
- R1.16 除数不可存在值为0的可能性
- R1.17 禁用atof、atoi、atol以及atoll等函数
- R1.18 格式化字符串应为常量
- R1.19 与程序实现相关的信息不可被外界感知
- R1.20 不应硬编码IP地址
- R1.21 避免使用errno
2. Resource
- R2.1 资源管理应遵循面向对象的方法
- R2.2 不可失去对已分配资源的控制
- R2.3 不可失去对已分配内存的控制
- R2.4 不可访问未初始化或已释放的资源
- R2.5 资源不可被重复释放
- R2.6 资源的分配与回收方法应配套使用
- R2.7 用new分配的单个对象应该用delete释放,不应该用delete[]释放
- R2.8 用new分配的数组应使用delete[]释放,不应使用delete释放
- R2.9 对象被move之后的值不应再被使用
- R2.10 对象申请的资源须在析构函数中释放
- R2.11 如果构造函数抛出异常需确保相关资源没有泄漏
- R2.12 C++代码中禁用malloc、free等内存分配与回收函数
- R2.13 在一个语句中最多执行一次显式资源分配
- R2.14 在栈上分配的空间以及非动态申请的资源不可被释放
- R2.15 避免使用在栈上分配内存的函数
- R2.16 避免不必要的内存分配
- R2.17 避免动态内存分配
- R2.18 标准FILE对象不应被复制
3. Precompile
- 3.1 Include
- R3.1.1 字母、数字、下划线以及点号之外的字符不应出现在头文件名称中
- R3.1.2 include指令中不应使用反斜杠
- R3.1.3 include指令中不应使用绝对路径
- R3.1.4 禁用不合规的头文件
- R3.1.5 在C++代码中不应引用C头文件
- 3.2 Macro
- R3.2.1 宏的命名应遵循合理的方式
- R3.2.2 不可定义具有保留意义的宏名称
- R3.2.3 不可取消定义具有保留意义的宏名称
- R3.2.4 可作为子表达式的宏定义应该用括号括起来
- R3.2.5 宏参数在宏定义的表达式中应该用括号括起来
- R3.2.6 由多个语句组成的宏定义应该用do-while(0)括起来
- R3.2.7 宏的实参个数不可小于形参个数
- R3.2.8 宏的实参个数不可大于形参个数
- R3.2.9 宏参数不应有副作用
- R3.2.10 宏参数数量应在规定范围之内
- R3.2.11 宏名称中不应存在拼写错误
- R3.2.12 不应使用宏定义常量
- R3.2.13 不应使用宏定义类型
- R3.2.14 可由函数实现的功能不应使用宏实现
- R3.2.15 在C++代码中不应使用宏offsetof
- R3.2.16 在宏定义中由#修饰的参数后不应出现##
- 3.3 Directive
- R3.3.1 头文件不应缺少守卫
- R3.3.2 不应出现非标准格式的预编译指令
- R3.3.3 不应使用非标准预编译指令
- R3.3.4 对编译警告的屏蔽应慎重
- R3.3.5 代码在高级别的警告设置下编译
- 3.4 Comment
- R3.4.1 关注TODO、FIXME、XXX、BUG等特殊注释
- R3.4.2 注释不可嵌套
- R3.4.3 注释应出现在合理的位置
- 3.5 Other
- R3.5.1 除转义字符、宏定义之外不应使用反斜杠
4. Global
- R4.1 不可修改std命名空间
- R4.2 非全局命名空间中不应存在以main命名的函数
- R4.3 全局对象的初始化不可依赖尚未初始化的其他对象
- R4.4 不应在头文件的全局作用域中使用using directive
- R4.5 头文件中不应定义匿名命名空间
- R4.6 全局或命名空间作用域中不应存在即不是const也不是static的对象
- R4.7 全局或命名空间作用域中不应存在非const对象
- R4.8 全局对象不应同时被static和const关键字修饰
- R4.9 匿名命名空间中不应使用静态声明
- R4.10 头文件中不应存在由static关键字修饰的非成员对象、数组或函数
- R4.11 命名空间作用域中禁用using namespace std之外的using directive
- R4.12 不应在命名空间中引用自身
- R4.13 不应定义全局inline命名空间
- R4.14 全局对象、函数、类型以及命名空间名称不应太短
5. Type
- 5.1 Class
- R5.1.1 类的非常量数据成员均应为private
- R5.1.2 类的非常量数据成员不应定义为protected
- R5.1.3 类不应即有public数据成员又有private数据成员
- R5.1.4 有虚函数的基类应具有虚析构函数
- R5.1.5 对于菱形继承应将基类设为虚基类
- R5.1.6 存在赋值运算符或析构函数时,不应缺少拷贝构造函数
- R5.1.7 存在拷贝构造函数或析构函数时,不应缺少拷贝赋值运算符
- R5.1.8 存在拷贝构造函数或赋值运算符时,不应缺少析构函数
- R5.1.9 存在移动构造函数时,不应缺少移动赋值运算符
- R5.1.10 存在移动赋值运算符,不应缺少移动构造函数
- R5.1.11 可接受一个参数的构造函数需用explicit关键字限定
- R5.1.12 重载的类型转换运算符需用explicit关键字限定
- R5.1.13 不应过度使用explicit关键字
- R5.1.14 带模板的赋值运算符不应覆盖拷贝或移动赋值运算符
- R5.1.15 带模板的构造函数不应覆盖拷贝或移动构造函数
- R5.1.16 重载的new和delete运算符应配对出现
- R5.1.17 抽象类禁用拷贝赋值运算符
- R5.1.18 数据成员的数量应在规定范围之内
- R5.1.19 存在构造、析构或虚函数的类不应采用struct关键字
- 5.2 Enum
- R5.2.1 同类枚举值不应有重复
- R5.2.2 各枚举值全不初始化,或者只初始化第一个,或者全部初始化,不应存在其他情况
- R5.2.3 不应使用匿名枚举声明
- R5.2.4 用enum class取代enum
- 5.3 Union
- R5.3.1 联合体内禁用非基本类型的对象
- R5.3.2 禁用在类之外定义的联合体
- R5.3.3 禁用联合体
6. Declaration
- 6.1 Naming
- R6.1.1 遵循合理的命名方式
- R6.1.2 不应定义具有保留意义的名称
- R6.1.3 局部名称不应被覆盖
- R6.1.4 成员名称不应被覆盖
- R6.1.5 同一命名空间内的类型名称不应与对象或函数名称相同
- R6.1.6 不应存在拼写错误
- 6.2 Qualifier
- R6.2.1 const、volatile不可修饰引用
- R6.2.2 const、volatile不应重复
- R6.2.3 const、volatile限定类型时应出现在左侧
- R6.2.4 const、volatile等关键字不应出现在基本类型名称的中间
- R6.2.5 指向常量字符串的指针应有const关键字修饰
- R6.2.6 enum的底层类型(underlying type)中不应出现const或volatile
- R6.2.7 对常量的定义不应为引用
- R6.2.8 慎用volatile关键字
- R6.2.9 禁用restrict指针
- 6.3 Specifier
- R6.3.1 使用auto关键字时需注意代码的可读性
- R6.3.2 不应使用已过时的关键字
- R6.3.3 不应使用多余的inline关键字
- R6.3.4 extern关键字不应作用于类成员的声明或定义
- R6.3.5 所有重写的虚函数都应声明为override或final
- R6.3.6 override和final关键字不应同时出现
- R6.3.7 当有override或final关键字时,不应再出现virtual关键字
- R6.3.8 不应将union设为final
- R6.3.9 inline、virtual、static、typedef等关键字应出现在类型名的左侧
- 6.4 Declarator
- R6.4.1 用auto声明指针或引用时应显式标明*、&等符号
- R6.4.2 禁用可变参数列表
- R6.4.3 禁用柔性数组
- R6.4.4 接口的参数或返回值不应被声明为void*
- R6.4.5 类成员不应被声明为void*
- R6.4.6 局部数组的长度不应过大
- R6.4.7 不应将函数或函数指针和其他声明写在同一个语句中
- R6.4.8 不建议将类型定义和对象声明写在一个语句中
- 6.5 Object
- R6.5.1 不应产生不受控制的无效临时对象
- R6.5.2 不应出现不会被用到的局部声明
- R6.5.3 对象初始化不可依赖自身的值
- R6.5.4 参与数值运算的char变量需显式声明signed或unsigned
- R6.5.5 表示二进制数据的char指针或数组应显式声明unsigned
- 6.6 Parameter
- R6.6.1 函数原型声明中的参数应具有合理的名称
- R6.6.2 不建议虚函数的参数有默认值
- R6.6.3 虚函数参数的默认值应与基类中声明的一致
- R6.6.4 不应将数组作为函数的形式参数
- R6.6.5 C代码中函数参数列表如果为空须声明为“(void)”
- R6.6.6 C++代码中函数参数列表如果为空不应声明为“(void)”
- R6.6.7 声明数组参数的大小时禁用static关键字
- 6.7 Function
- R6.7.1 派生类不应重新定义与基类相同的非虚函数
- R6.7.2 拷贝赋值、移动赋值运算符应返回所属类的非const引用
- R6.7.3 拷贝赋值运算符的参数应为同类对象的const左值引用
- R6.7.4 移动赋值运算符的参数应为同类对象的非const右值引用
- R6.7.5 不应重载取地址运算符
- R6.7.6 不应重载逗号运算符
- R6.7.7 不应重载“逻辑与”和“逻辑或”运算符
- R6.7.8 拷贝赋值、移动赋值运算符不应为虚函数
- R6.7.9 比较运算符不应为虚函数
- R6.7.10 final类中不应声明虚函数
- 6.8 Bitfield
- R6.8.1 位域长度不应超过类型约定的大小
- R6.8.2 有符号变量的位域长度不应为1
- R6.8.3 不应对枚举变量声明位域
- R6.8.4 禁用位域
- 6.9 Complexity
- R6.9.1 不建议采用复杂的声明
- R6.9.2 不建议采用复杂的参数声明
- R6.9.3 不建议在using别名中定义新的类
- R6.9.4 在一个语句中不应声明过多对象或函数
- 6.10 Other
- R6.10.1 不应存在没有用到的标签
- R6.10.2 不应存在未被使用的本地static函数
- R6.10.3 避免使用std::auto_ptr
7. Exception
- R7.1 确保异常的安全性
- R7.2 异常类的构造函数或异常信息相关的函数不应抛出异常
- R7.3 析构函数不可抛出异常
- R7.4 与STL标准库相关的hash过程不应抛出异常
- R7.5 对象的swap过程不可抛出异常
- R7.6 移动构造函数和移动赋值运算符不可抛出异常
- R7.7 禁用含throw关键字的异常规格说明
- R7.8 重新抛出异常时应使用空throw表达式(throw;)
- R7.9 不应在catch块外使用空throw表达式(throw;)
- R7.10 不应抛出过于宽泛的异常
- R7.11 不应抛出非异常类型的对象
- R7.12 不应将指针作为异常抛出
- R7.13 不应抛出NULL
- R7.14 不应抛出nullptr
- R7.15 禁用C++异常
8. Function
- R8.1 main函数返回值的类型只应为int
- R8.2 main函数不应被重载,也不应声明为inline、static或constexpr
- R8.3 函数不应在头文件中实现
- R8.4 不应定义过于复杂的内联函数
- R8.5 函数的参数名称在声明和实现处应保持一致
- R8.6 多态类的对象作为参数时不应采用值传递的方式
- R8.7 不应存在未被使用的具名形式参数
- R8.8 由const修饰的参数应为引用或指针
- R8.9 局部变量在使用前必须初始化
- R8.10 成员须在声明处或构造时初始化
- R8.11 基类对象构造完毕之前不可调用成员函数
- R8.12 在面向构造或析构函数体的catch块中不可访问非静态成员
- R8.13 成员初始化应遵循声明的顺序
- R8.14 在构造函数中不应调用虚函数
- R8.15 在析构函数中不应调用虚函数
- R8.16 拷贝构造函数应避免实现复制之外的功能
- R8.17 赋值运算符应妥善处理参数就是自身对象时的情况
- R8.18 不应存在得不到执行机会的代码
- R8.19 有返回值的函数其所有分枝都应有明确的返回值
- R8.20 不可返回局部对象的地址或引用
- R8.21 避免无效的写入
- R8.22 函数返回值不应为const对象
- R8.23 返回值应与函数的返回类型相符
- R8.24 函数返回值不应为相同的常量
- R8.25 基本类型的返回值不应使用const修饰
- R8.26 属性为noreturn的函数中不应出现return语句
- R8.27 属性为noreturn的函数返回类型只应为void
- R8.28 不应出现多余的跳转语句
- R8.29 va_start或va_copy应配合va_end使用
- R8.30 函数模版不应被特化
- R8.31 函数的标签数量应在规定范围之内
- R8.32 函数的行数应在规定范围之内
- R8.33 lambda表达式的行数应在规定范围之内
- R8.34 函数参数的数量应在规定范围之内
- R8.35 禁止goto语句向平级的或更深层的其他作用域跳转
- R8.36 禁止goto语句向前跳转
- R8.37 禁用goto语句
- R8.38 禁用setjmp、longjmp
- R8.39 避免递归实现
- R8.40 不应存在重复的函数实现
9. Control
- 9.1 If
- R9.1.1 if语句不应被分号隔断
- R9.1.2 在if...else-if分枝中不应有重复的条件
- R9.1.3 在if...else-if分枝中不应有被遮盖的条件
- R9.1.4 if分枝和else分枝的代码不应完全相同
- R9.1.5 if...else-if各分枝的代码不应完全相同
- R9.1.6 if分枝和其隐含的else分枝的代码不应完全相同
- R9.1.7 没有else子句的if语句与其后续代码相同是可疑的
- R9.1.8 if分枝和else分枝的起止语句不应相同
- R9.1.9 if语句作用域的范围不应有误
- R9.1.10 如果if关键字前面是右大括号,if关键字应另起一行
- R9.1.11 if语句的条件不应为赋值表达式
- R9.1.12 if语句不应为空
- R9.1.13 if...else-if分枝数量应在规定范围之内
- R9.1.14 if分枝中的语句应该用大括号括起来
- R9.1.15 所有if...else-if分枝都应以else子句结束
- 9.2 For
- R9.2.1 for语句不应被分号隔断
- R9.2.2 for循环中不应存在无条件的跳转语句
- R9.2.3 for语句作用域的范围不应有误
- R9.2.4 如果for语句没有明显的循环变量则应改为while循环
- R9.2.5 for循环体不应为空
- R9.2.6 for循环变量不应为浮点型
- R9.2.7 for循环变量不应在循环体内被改变
- R9.2.8 嵌套的for循环不应使用相同的循环变量
- R9.2.9 for循环体应该用大括号括起来
- 9.3 While
- R9.3.1 while语句不应被分号隔断
- R9.3.2 while语句中不应存在无条件的跳转语句
- R9.3.3 while语句的条件不应为赋值表达式
- R9.3.4 while语句作用域的范围不应有误
- R9.3.5 while循环体不应为空
- R9.3.6 while循环体应该用大括号括起来
- 9.4 Do
- R9.4.1 注意do-while(false)中可疑的continue语句
- R9.4.2 do-while循环体不应为空
- R9.4.3 do-while循环体应该用大括号括起来
- R9.4.4 不建议使用do语句
- 9.5 Switch
- R9.5.1 switch语句不应被分号隔断
- R9.5.2 switch语句不应为空
- R9.5.3 case常量的范围不可超出switch变量的范围
- R9.5.4 switch语句中任何子句都应从属于某个case或default分枝
- R9.5.5 每个case分枝应直接从属于switch结构
- R9.5.6 不应存在紧邻default标签的空case标签
- R9.5.7 不应存在内容完全相同的case分枝
- R9.5.8 switch语句的条件变量或表达式不应为bool型
- R9.5.9 不应使用只有default标签的switch语句
- R9.5.10 不应使用只有一个case标签的switch语句
- R9.5.11 switch语句分枝数量应在规定范围之内
- R9.5.12 switch语句应配有default分枝
- R9.5.13 每个非空的switch分枝都应该用无条件的break语句终止
- R9.5.14 switch语句应该用大括号括起来
- R9.5.15 switch语句不应嵌套
- 9.6 Try
- R9.6.1 不应存在空的try块
- R9.6.2 catch块序列中catch-all块(ellipsis handler)应位于最后
- R9.6.3 catch块序列中针对派生类的应排在前面,针对基类的应排在后面
- R9.6.4 try块不应嵌套
- 9.7 Catch
- R9.7.1 应通过引用捕获异常
- R9.7.2 捕获异常时不应产生对象切片问题
- R9.7.3 不应存在空的catch块
- R9.7.4 捕获异常后不应直接重新抛出异常,需对异常进行有效处理
- R9.7.5 不应捕获过于宽泛的异常
- R9.7.6 不应捕获非异常类型
10. Expression
- 10.1 Logic
- R10.1.1 不应出现不合逻辑的重复子表达式
- R10.1.2 逻辑表达式中各子表达式不应自相矛盾
- R10.1.3 条件表达式不应恒为真或恒为假
- R10.1.4 不应使用多余的逻辑子表达式
- R10.1.5 逻辑表达式及其子表达式的结果不应为常量
- R10.1.6 短路规则下不应存在有副作用的子表达式
- R10.1.7 逻辑表达式应尽量保持简洁明了
- R10.1.8 可化简为逻辑表达式的三元表达式应尽量化简
- 10.2 Arithmetic
- R10.2.1 不应存在没有效果的表达式
- R10.2.2 逗号表达式的子表达式应具有必要的副作用
- R10.2.3 赋值表达式中不应存在被赋值变量的自增或自减运算
- R10.2.4 子表达式的求值不应依赖特定的顺序
- R10.2.5 注意运算符优先级,不可产生非预期的结果
- R10.2.6 不在同一数组中的指针不可比较或相减
- R10.2.7 bool型变量或表达式不应参与大小比较、位运算、自增自减等运算
- R10.2.8 不应出现复合赋值的错误形式
- R10.2.9 避免出现复合赋值的可疑形式
- R10.2.10 &=、|=、-=、/=、%=左右子表达式不应相同
- R10.2.11 不应使用NULL对非指针变量赋值或初始化
- R10.2.12 赋值运算符与单目运算符之间应有空格,单目运算符与变量或表达式之间不应有空格
- R10.2.13 赋值运算符左右子表达式不应重复
- R10.2.14 除法运算符、求余运算符左右子表达不应重复
- R10.2.15 减法运算符左右子表达式不应重复
- R10.2.16 异或运算符左右子表达式不应重复
- R10.2.17 负号不应作用于无符号整数
- R10.2.18 不应重复使用一元运算符
- R10.2.19 运算结果不应溢出
- R10.2.20 位运算符不应作用于有符号整数
- R10.2.21 移位数量不可超过相关类型比特位的数量
- 10.3 Comparison
- R10.3.1 比较运算应在正确的范围内进行
- R10.3.2 不应使用==或!=判断浮点数是否相等
- R10.3.3 指针不应与字符串常量直接比较
- R10.3.4 不同类型的枚举值不应进行比较
- R10.3.5 比较运算符左右子表达式不应重复
- R10.3.6 比较运算不应作为另一个比较运算的直接子表达式
- 10.4 Call
- R10.4.1 返回值不应被忽略
- R10.4.2 不可臆断返回值的意义
- R10.4.3 避免对象切片
- R10.4.4 非基本类型的对象不应传入可变参数列表
- R10.4.5 C风格的格式化字符串与其参数的个数应严格一致
- R10.4.6 C风格的格式化字符串与其参数的类型应严格一致
- R10.4.7 不应显式调用析构函数
- R10.4.8 在C++代码中禁用C风格字符串格式化方法
- 10.5 Sizeof
- R10.5.1 sizeof不应作用于有副作用的表达式
- R10.5.2 sizeof的结果不应与0以及负数比较
- R10.5.3 对数组参数不应使用sizeof
- R10.5.4 sizeof不应作用于逻辑表达式
- R10.5.5 被除数不应是作用于指针的sizeof表达式
- R10.5.6 指针加减偏移量时计入sizeof是可疑的
- R10.5.7 sizeof不应再作用于sizeof
- R10.5.8 C++代码中sizeof不应作用于NULL
- R10.5.9 sizeof不可作用于void
- 10.6 Assertion
- R10.6.1 断言中的表达式不应恒为真
- R10.6.2 断言中的表达式不应有副作用
- R10.6.3 断言中的表达式不应过于复杂
- 10.7 Complexity
- R10.7.1 运算符不应超过规定数量
- 10.8 Other
- R10.8.1 不应访问填充数据
- R10.8.2 new表达式只可用于赋值或当作参数
- R10.8.3 数组下标应为整形表达式
- R10.8.4 禁用逗号表达式
- R10.8.5 不应使用多余的括号
11. Literal
- R11.1 注意可疑的字符常量
- R11.2 字符常量中不可存在应转义而未转义的字符
- R11.3 字符串常量中不可存在应转义而未转义的字符
- R11.4 不应使用非标准转义字符
- R11.5 不同前缀的字符串常量不可连接在一起
- R11.6 字符串常量中不应存在拼写错误
- R11.7 整数或浮点数常量的后缀应使用大写字母
- R11.8 禁用8进制常量
- R11.9 整数或浮点数常量应使用标准后缀
- R11.10 小心遗漏逗号导致的非预期字符串连接
- R11.11 不应存在magic number
- R11.12 不应存在magic string
- R11.13 不应使用多字符常量
12. Cast
- R12.1 避免类型转换造成的数据丢失
- R12.2 避免向下类型转换
- R12.3 指针与整数不应相互转换
- R12.4 类型转换时不应去掉const、volatile等属性
- R12.5 不应强制转换无继承关系的类型
- R12.6 不应强制转换非公有继承关系的类型
- R12.7 多态类型与基本类型不应相互转换
- R12.8 不可直接转换不同的字符串类型
- R12.9 避免类型转换造成的指针运算错误
- R12.10 对函数以及函数指针不应进行类型转换
- R12.11 向下类型转换时应使用dynamic_cast
- R12.12 对new表达式不应进行类型转换
- R12.13 不应存在多余的类型转换
- R12.14 可用static_cast、dynamic_cast完成的类型转换不可使用reinterpret_cast
- R12.15 在C++代码中禁用C风格类型转换
- R12.16 使用reinterpret_cast需有文档说明
13. Buffer
- R13.1 对内存的读写应在有效的边界内进行
- R13.2 数组下标不可越界
- R13.3 为缓冲区分配足够的空间
- R13.4 memset等函数不应作用于带有虚函数的对象
- R13.5 memset等函数长度相关的参数不应有误
- R13.6 memset等函数填充值相关的参数不应有误
14. Pointer
- R14.1 避免空指针解引用
- R14.2 注意逻辑表达式内的空指针解引用
- R14.3 应判断malloc等内存分配函数的返回值是否为空
- R14.4 用dynamic_cast转换指针时应判断结果是否为空
- R14.5 不可解引用已被释放的指针
- R14.6 不应将非零常量值赋值给指针
- R14.7 不应使用false等布尔常量对指针赋值或初始化
- R14.8 不应使用'\0'或L'\0'等字符常量对指针赋值或初始化
- R14.9 不应使用常数0对指针赋值
- R14.10 指针不应与bool型常量比较大小
- R14.11 指针不应与char型常量比较大小
- R14.12 不应判断指针大于、大于等于、小于、小于等于0
- R14.13 避免无效的空指针检查
- R14.14 不应重复检查指针是否为空
- R14.15 不应判断this指针是否为空
- R14.16 析构函数中不可使用delete this
- R14.17 禁用delete this
- R14.18 sizeof作用于指针是可疑的
- R14.19 指针在释放后应置空
15. Style
- R15.1 空格应遵循统一风格
- R15.2 大括号应遵循统一风格
- R15.3 NULL和nullptr不应混用
- R15.4 在C++代码中不应使用NULL,应使用nullptr
- R15.5 赋值表达式不应作为子表达式
- R15.6 不应存在多余的分号
C/C++安全规则集合 
Bjarne Stroustrup: “C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.”
针对C、C++语言,本文收录了409种需要重点关注的问题,可为制定编程规范提供依据,也可为代码审计以及相关培训提供指导意见,适用于桌面、服务端以及嵌入式等软件系统。
每个问题对应一条规则,每条规则可直接作为规范条款或审计检查点,本文是适用于不同应用场景的规则集合,读者可根据自身需求从中选取某个子集作为规范或审计依据,从而提高软件产品的安全性。
规则说明
规则按如下主题分为15个类别:
- Security:敏感数据保护、攻击防御等问题
- Resource:资源分配、使用与回收
- Precompile:预处理指令、宏、注释等问题
- Global:全局及命名空间作用域相关问题
- Type:类型相关的设计与实现
- Declaration:声明
- Exception:异常
- Function:函数实现
- Control:流程控制
- Expression:表达式
- Literal:常量
- Cast:类型转换
- Buffer:缓冲区
- Pointer:指针
- Style:样式、风格等问题
每条规则包括:
- 编号:规则在本文中的章节编号,以“R”开头,称为Section-ID
- 名称:用简练的短语描述违反规则的状况,以“ID_”开头,称为Fault-ID
- 标题:规则的定义
- 说明:规则设立的原因、示例、违反规则的后果、改进建议、参照依据、参考资料等内容
当代码出现违反规则的情况时,可分为:
- Error:可直接导致错误的问题
- Warning:可导致错误或存在隐患的问题
- Suspicious:代码的可疑形式
- Suggestion:对提高代码质量的建议
规则的说明包含:
- 示例:规则相关的示例代码,指明符合规则(Compliant)的和违反规则(Non-compliant)的情况
- 相关:与当前规则有相关性的规则,可作为扩展阅读的线索
- 依据:规则依照的ISO/IEC标准,C规则以ISO/IEC 9899:2011为主,C++规则以ISO/IEC 14882:2011为主
- 配置:某些规则的对象可由用户指定,审计工具可以此为参照实现定制化功能
- 参考:规则参考的其他规范条款,如C++ Core Guidelines、MISRA、CWE、SEI CERT等,也可作为扩展阅读的线索
规则的相关性分为:
- 特化:设规则A的特殊情况需要由规则B阐明,称规则B是规则A的特化
- 泛化:与特化相反,称规则A是规则B的泛化
- 相交:设两个规则针对不同的问题,但在内容上有一定的交集,称这两个规则相交
规则以“标准名称:版本 章节编号(段落编号)-性质”的格式引用标准,如“ISO/IEC 14882:2011 5.6(4)-undefined”,表示引用C++11标准的第5章第6节第4段说明的具有undefined性质的问题。
其中“性质”分为:
- undefined:一般指某种错误,使程序产生undefined behavior
- unspecified:标准不作明确规定的情况,由编译器或环境自主定义,具有随意性
- implementation defined:由实现定义,也是由编译器或环境自主定义,与unspecified不同,要求有明确的文档支持
- deprecated:已过时或已废弃的用法
本文以ISO/IEC 9899:2011以及ISO/IEC 14882:2011为主要依据,兼顾C18、C++17以及历史标准,没有特殊说明的规则同时适用于C语言和C++语言,只适用于某一种语言的规则会另有说明。
规则选取
本文是适用于不同应用场景的规则集合,读者可选取适合自己需求的规则。
指出某种错误的规则,如有“不可”、“不应”等字样的规则应尽量被选取,有“禁用”等字样的规则可能只适用于某一场景,可酌情选取。
如果将本文作为培训内容,为了全面理解各种场景下存在的问题,应选取全部规则。
规则列表
1. Security
- R1.1 敏感数据不应以明文形式写入代码
- R1.2 敏感数据不可被系统外界感知
- R1.3 敏感数据在使用后应被有效清理
- R1.4 公共成员或全局对象不应记录敏感数据
- R1.5 预判由用户输入造成的不良后果
- R1.6 避免在一个事务中通过路径多次访问同一文件
- R1.7 访问共享数据应遵循合理的同步机制
- R1.8 对文件设定合理的权限
- R1.9 对用户设置合理的权限
- R1.10 不应引用危险符号名称
- R1.11 避免调用具有危险性的函数
- R1.12 不应调用已过时的函数
- R1.13 禁用不安全的字符串函数
- R1.14 确保字符串以空字符结尾
- R1.15 避免使用由实现定义的库函数
- R1.16 除数不可存在值为0的可能性
- R1.17 禁用atof、atoi、atol以及atoll等函数
- R1.18 格式化字符串应为常量
- R1.19 与程序实现相关的信息不可被外界感知
- R1.20 不应硬编码IP地址
- R1.21 避免使用errno
2. Resource
- R2.1 资源管理应遵循面向对象的方法
- R2.2 不可失去对已分配资源的控制
- R2.3 不可失去对已分配内存的控制
- R2.4 不可访问未初始化或已释放的资源
- R2.5 资源不可被重复释放
- R2.6 资源的分配与回收方法应配套使用
- R2.7 用new分配的单个对象应该用delete释放,不应该用delete[]释放
- R2.8 用new分配的数组应使用delete[]释放,不应使用delete释放
- R2.9 对象被move之后的值不应再被使用
- R2.10 对象申请的资源须在析构函数中释放
- R2.11 如果构造函数抛出异常需确保相关资源没有泄漏
- R2.12 C++代码中禁用malloc、free等内存分配与回收函数
- R2.13 在一个语句中最多执行一次显式资源分配
- R2.14 在栈上分配的空间以及非动态申请的资源不可被释放
- R2.15 避免使用在栈上分配内存的函数
- R2.16 避免不必要的内存分配
- R2.17 避免动态内存分配
- R2.18 标准FILE对象不应被复制
3. Precompile
- 3.1 Include
- R3.1.1 字母、数字、下划线以及点号之外的字符不应出现在头文件名称中
- R3.1.2 include指令中不应使用反斜杠
- R3.1.3 include指令中不应使用绝对路径
- R3.1.4 禁用不合规的头文件
- R3.1.5 在C++代码中不应引用C头文件
- 3.2 Macro
- R3.2.1 宏的命名应遵循合理的方式
- R3.2.2 不可定义具有保留意义的宏名称
- R3.2.3 不可取消定义具有保留意义的宏名称
- R3.2.4 可作为子表达式的宏定义应该用括号括起来
- R3.2.5 宏参数在宏定义的表达式中应该用括号括起来
- R3.2.6 由多个语句组成的宏定义应该用do-while(0)括起来
- R3.2.7 宏的实参个数不可小于形参个数
- R3.2.8 宏的实参个数不可大于形参个数
- R3.2.9 宏参数不应有副作用
- R3.2.10 宏参数数量应在规定范围之内
- R3.2.11 宏名称中不应存在拼写错误
- R3.2.12 不应使用宏定义常量
- R3.2.13 不应使用宏定义类型
- R3.2.14 可由函数实现的功能不应使用宏实现
- R3.2.15 在C++代码中不应使用宏offsetof
- R3.2.16 在宏定义中由#修饰的参数后不应出现##
- 3.3 Directive
- R3.3.1 头文件不应缺少守卫
- R3.3.2 不应出现非标准格式的预编译指令
- R3.3.3 不应使用非标准预编译指令
- R3.3.4 对编译警告的屏蔽应慎重
- R3.3.5 代码在高级别的警告设置下编译
- 3.4 Comment
- R3.4.1 关注TODO、FIXME、XXX、BUG等特殊注释
- R3.4.2 注释不可嵌套
- R3.4.3 注释应出现在合理的位置
- 3.5 Other
- R3.5.1 除转义字符、宏定义之外不应使用反斜杠
4. Global
- R4.1 不可修改std命名空间
- R4.2 非全局命名空间中不应存在以main命名的函数
- R4.3 全局对象的初始化不可依赖尚未初始化的其他对象
- R4.4 不应在头文件的全局作用域中使用using directive
- R4.5 头文件中不应定义匿名命名空间
- R4.6 全局或命名空间作用域中不应存在即不是const也不是static的对象
- R4.7 全局或命名空间作用域中不应存在非const对象
- R4.8 全局对象不应同时被static和const关键字修饰
- R4.9 匿名命名空间中不应使用静态声明
- R4.10 头文件中不应存在由static关键字修饰的非成员对象、数组或函数
- R4.11 命名空间作用域中禁用using namespace std之外的using directive
- R4.12 不应在命名空间中引用自身
- R4.13 不应定义全局inline命名空间
- R4.14 全局对象、函数、类型以及命名空间名称不应太短
5. Type
- 5.1 Class
- R5.1.1 类的非常量数据成员均应为private
- R5.1.2 类的非常量数据成员不应定义为protected
- R5.1.3 类不应即有public数据成员又有private数据成员
- R5.1.4 有虚函数的基类应具有虚析构函数
- R5.1.5 对于菱形继承应将基类设为虚基类
- R5.1.6 存在赋值运算符或析构函数时,不应缺少拷贝构造函数
- R5.1.7 存在拷贝构造函数或析构函数时,不应缺少拷贝赋值运算符
- R5.1.8 存在拷贝构造函数或赋值运算符时,不应缺少析构函数
- R5.1.9 存在移动构造函数时,不应缺少移动赋值运算符
- R5.1.10 存在移动赋值运算符,不应缺少移动构造函数
- R5.1.11 可接受一个参数的构造函数需用explicit关键字限定
- R5.1.12 重载的类型转换运算符需用explicit关键字限定
- R5.1.13 不应过度使用explicit关键字
- R5.1.14 带模板的赋值运算符不应覆盖拷贝或移动赋值运算符
- R5.1.15 带模板的构造函数不应覆盖拷贝或移动构造函数
- R5.1.16 重载的new和delete运算符应配对出现
- R5.1.17 抽象类禁用拷贝赋值运算符
- R5.1.18 数据成员的数量应在规定范围之内
- R5.1.19 存在构造、析构或虚函数的类不应采用struct关键字
- 5.2 Enum
- R5.2.1 同类枚举值不应有重复
- R5.2.2 各枚举值全不初始化,或者只初始化第一个,或者全部初始化,不应存在其他情况
- R5.2.3 不应使用匿名枚举声明
- R5.2.4 用enum class取代enum
- 5.3 Union
- R5.3.1 联合体内禁用非基本类型的对象
- R5.3.2 禁用在类之外定义的联合体
- R5.3.3 禁用联合体
6. Declaration
- 6.1 Naming
- R6.1.1 遵循合理的命名方式
- R6.1.2 不应定义具有保留意义的名称
- R6.1.3 局部名称不应被覆盖
- R6.1.4 成员名称不应被覆盖
- R6.1.5 同一命名空间内的类型名称不应与对象或函数名称相同
- R6.1.6 不应存在拼写错误
- 6.2 Qualifier
- R6.2.1 const、volatile不可修饰引用
- R6.2.2 const、volatile不应重复
- R6.2.3 const、volatile限定类型时应出现在左侧
- R6.2.4 const、volatile等关键字不应出现在基本类型名称的中间
- R6.2.5 指向常量字符串的指针应有const关键字修饰
- R6.2.6 enum的底层类型(underlying type)中不应出现const或volatile
- R6.2.7 对常量的定义不应为引用
- R6.2.8 慎用volatile关键字
- R6.2.9 禁用restrict指针
- 6.3 Specifier
- R6.3.1 使用auto关键字时需注意代码的可读性
- R6.3.2 不应使用已过时的关键字
- R6.3.3 不应使用多余的inline关键字
- R6.3.4 extern关键字不应作用于类成员的声明或定义
- R6.3.5 所有重写的虚函数都应声明为override或final
- R6.3.6 override和final关键字不应同时出现
- R6.3.7 当有override或final关键字时,不应再出现virtual关键字
- R6.3.8 不应将union设为final
- R6.3.9 inline、virtual、static、typedef等关键字应出现在类型名的左侧
- 6.4 Declarator
- R6.4.1 用auto声明指针或引用时应显式标明*、&等符号
- R6.4.2 禁用可变参数列表
- R6.4.3 禁用柔性数组
- R6.4.4 接口的参数或返回值不应被声明为void*
- R6.4.5 类成员不应被声明为void*
- R6.4.6 局部数组的长度不应过大
- R6.4.7 不应将函数或函数指针和其他声明写在同一个语句中
- R6.4.8 不建议将类型定义和对象声明写在一个语句中
- 6.5 Object
- R6.5.1 不应产生不受控制的无效临时对象
- R6.5.2 不应出现不会被用到的局部声明
- R6.5.3 对象初始化不可依赖自身的值
- R6.5.4 参与数值运算的char变量需显式声明signed或unsigned
- R6.5.5 表示二进制数据的char指针或数组应显式声明unsigned
- 6.6 Parameter
- R6.6.1 函数原型声明中的参数应具有合理的名称
- R6.6.2 不建议虚函数的参数有默认值
- R6.6.3 虚函数参数的默认值应与基类中声明的一致
- R6.6.4 不应将数组作为函数的形式参数
- R6.6.5 C代码中函数参数列表如果为空须声明为“(void)”
- R6.6.6 C++代码中函数参数列表如果为空不应声明为“(void)”
- R6.6.7 声明数组参数的大小时禁用static关键字
- 6.7 Function
- R6.7.1 派生类不应重新定义与基类相同的非虚函数
- R6.7.2 拷贝赋值、移动赋值运算符应返回所属类的非const引用
- R6.7.3 拷贝赋值运算符的参数应为同类对象的const左值引用
- R6.7.4 移动赋值运算符的参数应为同类对象的非const右值引用
- R6.7.5 不应重载取地址运算符
- R6.7.6 不应重载逗号运算符
- R6.7.7 不应重载“逻辑与”和“逻辑或”运算符
- R6.7.8 拷贝赋值、移动赋值运算符不应为虚函数
- R6.7.9 比较运算符不应为虚函数
- R6.7.10 final类中不应声明虚函数
- 6.8 Bitfield
- R6.8.1 位域长度不应超过类型约定的大小
- R6.8.2 有符号变量的位域长度不应为1
- R6.8.3 不应对枚举变量声明位域
- R6.8.4 禁用位域
- 6.9 Complexity
- R6.9.1 不建议采用复杂的声明
- R6.9.2 不建议采用复杂的参数声明
- R6.9.3 不建议在using别名中定义新的类
- R6.9.4 在一个语句中不应声明过多对象或函数
- 6.10 Other
- R6.10.1 不应存在没有用到的标签
- R6.10.2 不应存在未被使用的本地static函数
- R6.10.3 避免使用std::auto_ptr
7. Exception
- R7.1 确保异常的安全性
- R7.2 异常类的构造函数或异常信息相关的函数不应抛出异常
- R7.3 析构函数不可抛出异常
- R7.4 与STL标准库相关的hash过程不应抛出异常
- R7.5 对象的swap过程不可抛出异常
- R7.6 移动构造函数和移动赋值运算符不可抛出异常
- R7.7 禁用含throw关键字的异常规格说明
- R7.8 重新抛出异常时应使用空throw表达式(throw;)
- R7.9 不应在catch块外使用空throw表达式(throw;)
- R7.10 不应抛出过于宽泛的异常
- R7.11 不应抛出非异常类型的对象
- R7.12 不应将指针作为异常抛出
- R7.13 不应抛出NULL
- R7.14 不应抛出nullptr
- R7.15 禁用C++异常
8. Function
- R8.1 main函数返回值的类型只应为int
- R8.2 main函数不应被重载,也不应声明为inline、static或constexpr
- R8.3 函数不应在头文件中实现
- R8.4 不应定义过于复杂的内联函数
- R8.5 函数的参数名称在声明和实现处应保持一致
- R8.6 多态类的对象作为参数时不应采用值传递的方式
- R8.7 不应存在未被使用的具名形式参数
- R8.8 由const修饰的参数应为引用或指针
- R8.9 局部变量在使用前必须初始化
- R8.10 成员须在声明处或构造时初始化
- R8.11 基类对象构造完毕之前不可调用成员函数
- R8.12 在面向构造或析构函数体的catch块中不可访问非静态成员
- R8.13 成员初始化应遵循声明的顺序
- R8.14 在构造函数中不应调用虚函数
- R8.15 在析构函数中不应调用虚函数
- R8.16 拷贝构造函数应避免实现复制之外的功能
- R8.17 赋值运算符应妥善处理参数就是自身对象时的情况
- R8.18 不应存在得不到执行机会的代码
- R8.19 有返回值的函数其所有分枝都应有明确的返回值
- R8.20 不可返回局部对象的地址或引用
- R8.21 避免无效的写入
- R8.22 函数返回值不应为const对象
- R8.23 返回值应与函数的返回类型相符
- R8.24 函数返回值不应为相同的常量
- R8.25 基本类型的返回值不应使用const修饰
- R8.26 属性为noreturn的函数中不应出现return语句
- R8.27 属性为noreturn的函数返回类型只应为void
- R8.28 不应出现多余的跳转语句
- R8.29 va_start或va_copy应配合va_end使用
- R8.30 函数模版不应被特化
- R8.31 函数的标签数量应在规定范围之内
- R8.32 函数的行数应在规定范围之内
- R8.33 lambda表达式的行数应在规定范围之内
- R8.34 函数参数的数量应在规定范围之内
- R8.35 禁止goto语句向平级的或更深层的其他作用域跳转
- R8.36 禁止goto语句向前跳转
- R8.37 禁用goto语句
- R8.38 禁用setjmp、longjmp
- R8.39 避免递归实现
- R8.40 不应存在重复的函数实现
9. Control
- 9.1 If
- R9.1.1 if语句不应被分号隔断
- R9.1.2 在if...else-if分枝中不应有重复的条件
- R9.1.3 在if...else-if分枝中不应有被遮盖的条件
- R9.1.4 if分枝和else分枝的代码不应完全相同
- R9.1.5 if...else-if各分枝的代码不应完全相同
- R9.1.6 if分枝和其隐含的else分枝的代码不应完全相同
- R9.1.7 没有else子句的if语句与其后续代码相同是可疑的
- R9.1.8 if分枝和else分枝的起止语句不应相同
- R9.1.9 if语句作用域的范围不应有误
- R9.1.10 如果if关键字前面是右大括号,if关键字应另起一行
- R9.1.11 if语句的条件不应为赋值表达式
- R9.1.12 if语句不应为空
- R9.1.13 if...else-if分枝数量应在规定范围之内
- R9.1.14 if分枝中的语句应该用大括号括起来
- R9.1.15 所有if...else-if分枝都应以else子句结束
- 9.2 For
- R9.2.1 for语句不应被分号隔断
- R9.2.2 for循环中不应存在无条件的跳转语句
- R9.2.3 for语句作用域的范围不应有误
- R9.2.4 如果for语句没有明显的循环变量则应改为while循环
- R9.2.5 for循环体不应为空
- R9.2.6 for循环变量不应为浮点型
- R9.2.7 for循环变量不应在循环体内被改变
- R9.2.8 嵌套的for循环不应使用相同的循环变量
- R9.2.9 for循环体应该用大括号括起来
- 9.3 While
- R9.3.1 while语句不应被分号隔断
- R9.3.2 while语句中不应存在无条件的跳转语句
- R9.3.3 while语句的条件不应为赋值表达式
- R9.3.4 while语句作用域的范围不应有误
- R9.3.5 while循环体不应为空
- R9.3.6 while循环体应该用大括号括起来
- 9.4 Do
- R9.4.1 注意do-while(false)中可疑的continue语句
- R9.4.2 do-while循环体不应为空
- R9.4.3 do-while循环体应该用大括号括起来
- R9.4.4 不建议使用do语句
- 9.5 Switch
- R9.5.1 switch语句不应被分号隔断
- R9.5.2 switch语句不应为空
- R9.5.3 case常量的范围不可超出switch变量的范围
- R9.5.4 switch语句中任何子句都应从属于某个case或default分枝
- R9.5.5 每个case分枝应直接从属于switch结构
- R9.5.6 不应存在紧邻default标签的空case标签
- R9.5.7 不应存在内容完全相同的case分枝
- R9.5.8 switch语句的条件变量或表达式不应为bool型
- R9.5.9 不应使用只有default标签的switch语句
- R9.5.10 不应使用只有一个case标签的switch语句
- R9.5.11 switch语句分枝数量应在规定范围之内
- R9.5.12 switch语句应配有default分枝
- R9.5.13 每个非空的switch分枝都应该用无条件的break语句终止
- R9.5.14 switch语句应该用大括号括起来
- R9.5.15 switch语句不应嵌套
- 9.6 Try
- R9.6.1 不应存在空的try块
- R9.6.2 catch块序列中catch-all块(ellipsis handler)应位于最后
- R9.6.3 catch块序列中针对派生类的应排在前面,针对基类的应排在后面
- R9.6.4 try块不应嵌套
- 9.7 Catch
- R9.7.1 应通过引用捕获异常
- R9.7.2 捕获异常时不应产生对象切片问题
- R9.7.3 不应存在空的catch块
- R9.7.4 捕获异常后不应直接重新抛出异常,需对异常进行有效处理
- R9.7.5 不应捕获过于宽泛的异常
- R9.7.6 不应捕获非异常类型
10. Expression
- 10.1 Logic
- R10.1.1 不应出现不合逻辑的重复子表达式
- R10.1.2 逻辑表达式中各子表达式不应自相矛盾
- R10.1.3 条件表达式不应恒为真或恒为假
- R10.1.4 不应使用多余的逻辑子表达式
- R10.1.5 逻辑表达式及其子表达式的结果不应为常量
- R10.1.6 短路规则下不应存在有副作用的子表达式
- R10.1.7 逻辑表达式应尽量保持简洁明了
- R10.1.8 可化简为逻辑表达式的三元表达式应尽量化简
- 10.2 Arithmetic
- R10.2.1 不应存在没有效果的表达式
- R10.2.2 逗号表达式的子表达式应具有必要的副作用
- R10.2.3 赋值表达式中不应存在被赋值变量的自增或自减运算
- R10.2.4 子表达式的求值不应依赖特定的顺序
- R10.2.5 注意运算符优先级,不可产生非预期的结果
- R10.2.6 不在同一数组中的指针不可比较或相减
- R10.2.7 bool型变量或表达式不应参与大小比较、位运算、自增自减等运算
- R10.2.8 不应出现复合赋值的错误形式
- R10.2.9 避免出现复合赋值的可疑形式
- R10.2.10 &=、|=、-=、/=、%=左右子表达式不应相同
- R10.2.11 不应使用NULL对非指针变量赋值或初始化
- R10.2.12 赋值运算符与单目运算符之间应有空格,单目运算符与变量或表达式之间不应有空格
- R10.2.13 赋值运算符左右子表达式不应重复
- R10.2.14 除法运算符、求余运算符左右子表达不应重复
- R10.2.15 减法运算符左右子表达式不应重复
- R10.2.16 异或运算符左右子表达式不应重复
- R10.2.17 负号不应作用于无符号整数
- R10.2.18 不应重复使用一元运算符
- R10.2.19 运算结果不应溢出
- R10.2.20 位运算符不应作用于有符号整数
- R10.2.21 移位数量不可超过相关类型比特位的数量
- 10.3 Comparison
- R10.3.1 比较运算应在正确的范围内进行
- R10.3.2 不应使用==或!=判断浮点数是否相等
- R10.3.3 指针不应与字符串常量直接比较
- R10.3.4 不同类型的枚举值不应进行比较
- R10.3.5 比较运算符左右子表达式不应重复
- R10.3.6 比较运算不应作为另一个比较运算的直接子表达式
- 10.4 Call
- R10.4.1 返回值不应被忽略
- R10.4.2 不可臆断返回值的意义
- R10.4.3 避免对象切片
- R10.4.4 非基本类型的对象不应传入可变参数列表
- R10.4.5 C风格的格式化字符串与其参数的个数应严格一致
- R10.4.6 C风格的格式化字符串与其参数的类型应严格一致
- R10.4.7 不应显式调用析构函数
- R10.4.8 在C++代码中禁用C风格字符串格式化方法
- 10.5 Sizeof
- R10.5.1 sizeof不应作用于有副作用的表达式
- R10.5.2 sizeof的结果不应与0以及负数比较
- R10.5.3 对数组参数不应使用sizeof
- R10.5.4 sizeof不应作用于逻辑表达式
- R10.5.5 被除数不应是作用于指针的sizeof表达式
- R10.5.6 指针加减偏移量时计入sizeof是可疑的
- R10.5.7 sizeof不应再作用于sizeof
- R10.5.8 C++代码中sizeof不应作用于NULL
- R10.5.9 sizeof不可作用于void
- 10.6 Assertion
- R10.6.1 断言中的表达式不应恒为真
- R10.6.2 断言中的表达式不应有副作用
- R10.6.3 断言中的表达式不应过于复杂
- 10.7 Complexity
- R10.7.1 运算符不应超过规定数量
- 10.8 Other
- R10.8.1 不应访问填充数据
- R10.8.2 new表达式只可用于赋值或当作参数
- R10.8.3 数组下标应为整形表达式
- R10.8.4 禁用逗号表达式
- R10.8.5 不应使用多余的括号
11. Literal
- R11.1 注意可疑的字符常量
- R11.2 字符常量中不可存在应转义而未转义的字符
- R11.3 字符串常量中不可存在应转义而未转义的字符
- R11.4 不应使用非标准转义字符
- R11.5 不同前缀的字符串常量不可连接在一起
- R11.6 字符串常量中不应存在拼写错误
- R11.7 整数或浮点数常量的后缀应使用大写字母
- R11.8 禁用8进制常量
- R11.9 整数或浮点数常量应使用标准后缀
- R11.10 小心遗漏逗号导致的非预期字符串连接
- R11.11 不应存在magic number
- R11.12 不应存在magic string
- R11.13 不应使用多字符常量
12. Cast
- R12.1 避免类型转换造成的数据丢失
- R12.2 避免向下类型转换
- R12.3 指针与整数不应相互转换
- R12.4 类型转换时不应去掉const、volatile等属性
- R12.5 不应强制转换无继承关系的类型
- R12.6 不应强制转换非公有继承关系的类型
- R12.7 多态类型与基本类型不应相互转换
- R12.8 不可直接转换不同的字符串类型
- R12.9 避免类型转换造成的指针运算错误
- R12.10 对函数以及函数指针不应进行类型转换
- R12.11 向下类型转换时应使用dynamic_cast
- R12.12 对new表达式不应进行类型转换
- R12.13 不应存在多余的类型转换
- R12.14 可用static_cast、dynamic_cast完成的类型转换不可使用reinterpret_cast
- R12.15 在C++代码中禁用C风格类型转换
- R12.16 使用reinterpret_cast需有文档说明
13. Buffer
- R13.1 对内存的读写应在有效的边界内进行
- R13.2 数组下标不可越界
- R13.3 为缓冲区分配足够的空间
- R13.4 memset等函数不应作用于带有虚函数的对象
- R13.5 memset等函数长度相关的参数不应有误
- R13.6 memset等函数填充值相关的参数不应有误
14. Pointer
- R14.1 避免空指针解引用
- R14.2 注意逻辑表达式内的空指针解引用
- R14.3 应判断malloc等内存分配函数的返回值是否为空
- R14.4 用dynamic_cast转换指针时应判断结果是否为空
- R14.5 不可解引用已被释放的指针
- R14.6 不应将非零常量值赋值给指针
- R14.7 不应使用false等布尔常量对指针赋值或初始化
- R14.8 不应使用'\0'或L'\0'等字符常量对指针赋值或初始化
- R14.9 不应使用常数0对指针赋值
- R14.10 指针不应与bool型常量比较大小
- R14.11 指针不应与char型常量比较大小
- R14.12 不应判断指针大于、大于等于、小于、小于等于0
- R14.13 避免无效的空指针检查
- R14.14 不应重复检查指针是否为空
- R14.15 不应判断this指针是否为空
- R14.16 析构函数中不可使用delete this
- R14.17 禁用delete this
- R14.18 sizeof作用于指针是可疑的
- R14.19 指针在释放后应置空
15. Style
- R15.1 空格应遵循统一风格
- R15.2 大括号应遵循统一风格
- R15.3 NULL和nullptr不应混用
- R15.4 在C++代码中不应使用NULL,应使用nullptr
- R15.5 赋值表达式不应作为子表达式
- R15.6 不应存在多余的分号
更多文章请关注《万象专栏》
转载请注明出处:https://www.wanxiangsucai.com/read/cv80127