【第二部分】写出军规级代码:JSFAVC++编码标准

admin 2026-07-10 07:21:49 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细阐述了JSFAVC++编码标准,涵盖头文件规范、实现文件要求、代码风格指南及类设计原则。核心规则包括头文件包含符号规范、防止重复包含、最小化编译依赖、标识符命名约定、文件命名规范、类访问控制顺序、控制语句花括号使用等。旨在通过严格的编码规则提升代码质量、可读性和可维护性。 综合评分: 85 文章分类: 安全建设,代码审计,安全开发,技术标准,其他


cover_image

【第二部分】写出军规级代码:JSF AV C++编码标准

原创

crackme.net crackme.net

crackme安全实验室

2026年7月6日 13:30 河南

在小说阅读器读本章

去阅读

头文件

AV Rule 33:头文件包含符号规范

规定(Shall):#include 指令必须使用 <filename.h> 形式来包含头文件,允许使用相对路径。

理由:尽管 "filename.h" 形式通常用于本地头文件,但由于不同编译器厂商实现的差异,统一使用 <filename.h> 形式可避免兼容性问题。

AV Rule 34:头文件内容逻辑相关性

规定(Should):头文件应仅包含逻辑上相关的声明。

理由:最小化不必要的依赖关系。

AV Rule 35:防止头文件重复包含

规定(Will):每个头文件必须包含防止自身被多次包含的机制。

理由:避免意外的头文件递归包含。

备注:具体防重入机制由 AV Rule 27 指定,本规则强调必须使用该机制。

AV Rule 36:最小化编译依赖

规定(Should):应尽可能减少编译依赖。

理由:减少源文件不必要的重新编译。

AV Rule 37:头文件包含的最小化原则

规定(Should):头文件应仅包含使其成功编译所必需的头文件;仅被关联 .cpp 文件使用的头文件应放在 .cpp 文件中,而非 .h 文件中。

理由:头文件中的 #include 语句定义了文件的依赖关系。依赖越少意味着耦合越松,当头文件需要修改时产生的连锁反应越小。

AV Rule 38:使用前置声明替代头文件包含

规定(Should):对于仅通过指针或引用访问的类,应通过仅包含前置声明的前置头文件来提供声明。

理由:仅通过指针或引用引用的类无需包含其完整头文件。包含完整头文件会增加类间耦合、编译依赖及维护成本。使用前置声明可限制实现依赖、降低维护工作量并缩短编译时间。

AV Rule 39:禁止在头文件中定义非 const 变量或函数

规定(Will):头文件不得包含非 const 变量定义或函数定义。

理由:头文件通常应包含接口声明,而非实现细节。

例外情况:内联函数和模板定义可以包含在头文件中。

实现文件

AV Rule 40:实现文件的头文件包含要求

规定(Shall):每个实现文件必须包含唯一其所使用的内联函数、类型和模板定义的头文件。

理由:确保编译器能够进行一致性检查,同时隐含要求特定的内联函数、类型或模板的定义不得出现在多个头文件中。

代码风格

AV Rule 41:源文件行长度限制

规定(Will):源代码行的长度必须保持在120个字符或更少。

理由:可读性与代码风格。过长的源代码行难以阅读和理解。

AV Rule 42:表达式语句独立成行

规定(Shall):每个表达式语句必须单独占据一行。

理由:简洁性、可读性与代码风格。

AV Rule 43:避免使用 Tab 字符

规定(Should):应当避免在代码中使用Tab制表符。

理由:Tab 字符在不同的编辑器和打印机中会被以不同方式解释,导致显示不一致。许多编辑器可以配置为将 Tab 键映射为指定数量的空格。

AV Rule 44:缩进规范

规定(Shall):所有缩进必须至少为两个空格,并且在同一个源文件中保持一致。

理由:可读性与代码风格。

标识符命名

标识符的命名应:

  • • 暗示标识符的用途。
  • • 由简短但具有描述性的名称组成。
  • • 长度足以避免名称冲突,但不要过长。
  • • 包含普遍接受的缩写。

例外情况:在小作用域中使用简单标识符(如 i、x、y、p 等)的传统用法可以使代码更简洁,因此是允许的。

AV Rule 45:标识符单词分隔

规定(Will):标识符中的所有单词必须使用下划线字符 _ 进行分隔。

理由:可读性与代码风格。

AV Rule 46:标识符有效字符长度限制

规定(Will):用户定义的标识符(包括内部和外部标识符)不得超过 64 个字符。

理由:确保编译器兼容性。尽管 C++ 标准建议至少支持 1,024 个有效字符,但本规则采用更严格的限制以保证可移植性。

AV Rule 47:禁止以下划线开头

规定(Will):标识符不得以下划线字符 _ 开头。

理由:下划线常被用作库函数名称的首字符(如 _main_exit 等),为避免命名冲突,标识符不应以下划线开头。

AV Rule 48:标识符区分度要求

规定(Will):标识符之间不得仅因以下差异而区分:

  • • 仅大小写混合不同
  • • 下划线字符的有无
  • • 字母 O 与数字 0 或字母 D 互换
  • • 字母 I 与数字 1 或字母 l 互换
  • • 字母 S 与数字 5 互换
  • • 字母 Z 与数字 2 互换
  • • 字母 n 与字母 h 互换

理由:可读性,防止视觉混淆导致的错误。

AV Rule 49:缩写词大写规范

规定(Will):标识符中的所有缩写词必须由大写字母组成。

理由:可读性。

备注:即使根据其他规则该标识符部分应使用小写,缩写词仍必须始终保持大写。

AV Rule 50:类、结构体、枚举类型和 typedef 的命名

规定(Will):类、结构体、命名空间、枚举或使用 typedef 创建的类型,其名称的第一个单词必须以大写字母开头,其余所有字母必须为小写。

理由:代码风格。

例外情况:为了符合标准库接口或作为基本类型的替代时(参见 AV Rule 209),typedef 名称的首字母允许使用小写。

class&nbsp;Diagonal_matrix&nbsp;{ … }; &nbsp;// 只有首字母大写
enum&nbsp;RGB_colors&nbsp;{red, green, blue}; &nbsp;// RGB 是一个首字母缩略词,因此所有字母均使用大写

// 例外情况
typename&nbsp;C::value_type s =&nbsp;0; &nbsp;// 容器 C 的 value_type 以小写字母开头,以符合标准库中 typedef 的命名规范

AV Rule 51:函数、变量和参数的命名

规定(Will):函数名和变量名中包含的所有字母必须完全由小写字母组成。

理由:代码风格。

class&nbsp;Example_class_name
{
public:
&nbsp; &nbsp;&nbsp;uint16&nbsp;example_function_name(void);
private:
&nbsp; &nbsp; uint16 example_variable_name;
};

AV Rule 52:常量和枚举值的命名

规定(Shall):常量和枚举值的标识符应当使用小写字母。

理由:尽管使用大写字母表示常量和枚举值是常见惯例,但第三方库可能会在宏替换过程中替换常量/枚举值名称(宏通常也使用大写字母表示),使用小写可避免此类冲突。

const&nbsp;uint16 max_pressure =&nbsp;100;
enum&nbsp;Switch_position&nbsp;{up, down};

文件命名

AV Rule 53:头文件扩展名规范

规定(Will):头文件必须始终使用 .h 作为文件扩展名。

AV Rule 53.1:头文件名禁用字符序列

规定(Shall):头文件名中不得出现以下字符序列: \ /* // "

理由:若头文件名中包含上述字符序列,将导致未定义行为。允许使用相对路径,但目录与文件名之间仅能使用 / 作为分隔符。

#include&nbsp;<foo /* comment */ .h>&nbsp; &nbsp;// 错误:出现了 “/*”
#include&nbsp;<foo’s .h>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 错误:出现了 “’”
#include&nbsp;<dir1\dir2\foo.h>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 错误:出现了 “\”

#include&nbsp;<dir1/dir2/foo.h>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 最佳实践

AV Rule 54:实现文件扩展名规范

规定(Will):实现文件必须始终使用 .cpp 作为文件扩展名。

AV Rule 55:头文件命名语义化

规定(Should):头文件的名称应当反映其所提供声明的逻辑实体。

// 对于 Matrix 实体,其头文件的名称应为
Matrix.h

AV Rule 56:实现文件命名语义化

规定(Should):实现文件的名称应当反映其所提供定义的逻辑实体,并使用 .cpp 扩展名;通常该名称应与对应声明的头文件名相同。当同一逻辑实体需要多个实现文件时,应添加后缀以体现逻辑区分。

// Matrix 类的单个 .cpp 文件
Matrix.cpp

// 数学库的多个文件
Math_sqrt.cpp
Math_sin.cpp
Math_cos.cpp

AV Rule 57:类访问控制声明顺序

规定(Will):类的 public、 protected 和 private 部分必须按此顺序声明(即 public 部分在 protected 部分之前, protected 部分在 private 部分之前)。

理由:将 public 部分置于首位,可使用户关注的内容集中在类定义的开头; protected 部分供继承设计者参考;private 部分包含实现细节,通用关注度最低。该顺序符合从接口到实现的阅读逻辑。

AV Rule 58:多参数函数声明与定义格式

规定(Will):当声明或定义具有两个以上参数的函数时,左括号和第一个参数必须与函数名写在同一行;每个后续参数必须单独占一行,右括号紧跟在最后一个参数之后。

理由:可读性与代码风格。

代码块

AV Rule 59:控制语句使用花括号

规定(Shall):构成 if 、else if 、else 、while 、do…while 或 for 语句体的语句必须始终用花括号括起来,即使花括号内是空块也必须如此。

理由:提高可读性,避免单独出现的分号“;”难以辨识而引发误解或错误。

AV Rule 60:花括号垂直对齐与独立成行

规定(Will):用于包裹代码块的花括号“{}”必须位于同一列,并分别独占一行,紧接在代码块的前后。

if&nbsp;(var_name ==&nbsp;true)
{
}
else
{
}

AV Rule 61:花括号所在行内容限制

规定(Will):包裹代码块的花括号 {} 所在行除必要的注释外,不得包含任何其他代码或内容。允许在花括号同行添加必要注释。

AV Rule 62:指针与引用操作符绑定类型

规定(Will):解引用操作符 * 和取地址操作符 & 必须直接与类型说明符相连。

理由:C++语言强调类型系统,将操作符紧贴类型(如int32* p;)能突出“指针是一种类型”的语义;而紧贴变量名(如int32 *p;)则侧重于语法解析,不符合C++的类型导向设计理念。

备注:根据 AV Rule 152 要求的“每次声明仅一个变量”风格,不会出现 int32* p, q; 这类易引发歧义的混合声明形式。)

int32* p; &nbsp; &nbsp;&nbsp;// 正确
int32 *p; &nbsp; &nbsp;&nbsp;// 错误
int32* p, q; &nbsp;// 错误(AV Rule 152)

AV Rule 63:成员访问与一元操作符空格限制

规定(Will):在 . 或 -> 操作符周围不得使用空格,一元操作符与其操作数之间也不得使用空格。

AV Rule 64:类接口完整性与最小化

规定(Should):类的接口应当既完整又最小化。

理由:完整的接口确保客户端能够执行所有合理的操作;最小化的接口则保证函数数量尽可能少且无功能重叠,从而在满足合理需求的前提下避免不必要的复杂性。

例外情况:当效率要求必须提供重叠服务时允许例外;此外,使用辅助函数也可作为简化类接口的有效手段。

访问权限

AV Rule 65 & 66:结构体与类的建模选择

规定(Should):当建模的实体不需要维护不变量时,应使用结构体;当实体需要维护不变量时,应使用类。

AV Rule 67:公有与保护数据的限制

规定(Should):公有和保护数据成员仅允许在结构体中使用,不得在类中使用。

理由:类通过控制数据访问来维护不变量,若数据成员非私有,则无法保证对访问的有效控制,因此类中的所有数据成员应为私有。

例外情况:当类不参与客户端接口时,允许使用保护数据成员(AV Rule 88)。

AV Rule 68:显式禁用不需要的隐式生成成员函数

规定(Shall):必须显式禁止不需要的编译器隐式生成成员函数。

理由:消除因编译器自动生成函数而可能引发的意外行为。例如,若某类不需要赋值操作符却未显式禁用,编译器会自动生成并执行默认版本,可能导致非预期结果;显式将其声明为私有且不提供定义,可使任何调用在编译期报错,从而避免运行时隐患。

备注:若显式禁用了拷贝构造函数,则赋值操作符也应一并禁用

AV Rule 69:成员函数的 const 限定

规定(Will):不修改对象状态(实例变量)的成员函数必须声明为const;成员函数应默认声明为const,仅在存在明确且显式的理由时才可省略const修饰符。

理由:声明成员函数为const可确保对象在不应被修改时不会被意外更改;同时,C++支持基于const特性的函数重载,有助于提供更精确的接口语义和编译期安全检查。

例外情况:当存在明确、显式的设计理由需要函数具备非const行为时,允许省略const修饰符。

AV Rule 70:友元的使用限制

规定(Will):仅当函数或对象需要访问类的私有成员,且因逻辑或效率原因无法成为该类的成员时,才允许将类声明为友元。

理由:过度使用友元会导致代码难以理解和维护

例外情况:当避免使用友元会导致暴露不必要的内部实现细节时,应优先使用友元而非扩大公有接口。

生命周期 构造函数 析构函数

AV Rule 70.1:对象生命周期约束

规定(Shall):禁止在对象生命周期开始之前或结束之后对其进行不当使用。

理由:在对象创建前或销毁后对其进行不当访问会导致未定义行为。

构造函数

AV Rule 71:对象完全初始化前禁止调用外部操作

规定(Shall):在对象完全初始化之前,禁止调用该对象除构造函数以外的任何外部可见操作。

理由:防止因对象初始化不完整而导致的运行时错误或未定义行为。

AV Rule 71.1:构造与析构函数中禁止调用虚函数

规定(Shall):禁止在类的构造函数或析构函数中调用该类的虚函数。

理由:在构造函数和析构函数执行期间,虚函数的解析是静态的而非动态的,无法实现预期的多态行为,可能导致逻辑错误。

AV Rule 72:类不变式的维护

规定(Should):类的不变式应作为每个构造函数的后置条件、析构函数的前置条件(如有),以及所有其他公有可访问操作的前置条件和后置条件的一部分。

理由:确保客户端只能通过公有接口影响对象状态,从而保证对象始终处于有效状态,防止内部一致性被破坏。

AV Rule 73:避免定义不必要的默认构造函数

规定(Shall):不应定义不必要的默认构造函数。

理由:防止对象在缺乏必要数据的情况下被创建,避免对象处于部分初始化的无效状态,鼓励在数据完备时再进行构造。

AV Rule 74:使用成员初始化列表进行初始化

规定(Will):非静态类成员的初始化必须通过成员初始化列表完成,而非在构造函数体内通过赋值完成。

理由:成员初始化列表直接构造成员对象,避免了先默认构造再赋值的额外开销,且对于const成员、引用成员及无默认构造函数的类型是必须的。

例外情况:当初始值无法用简单表达式表示(如数组初始化),或需要先引入变量名才能获取初始值(如从输入流读取)时,允许使用赋值方式。

AV Rule 75:初始化列表顺序与声明顺序一致

规定(Shall):成员初始化列表中的成员排列顺序必须与其在类中声明的顺序一致;基类初始化器应出现在列表最前面。

理由:C++ 标准规定成员按声明顺序初始化,而非按初始化列表中的书写顺序

AV Rule 76:含指针或非平凡析构函数的类需定义拷贝语义

规定(Shall):包含指向数据成员的指针或具有非平凡析构函数的类,必须显式声明拷贝构造函数和赋值运算符。

理由:确保在拷贝和赋值操作中资源得到正确管理,防止浅拷贝导致的资源泄漏或双重释放问题。

例外情况:当默认的拷贝和赋值运算符能提供合理的语义时,优先使用默认实现(AV Rule 80)。

AV Rule 77:拷贝构造函数需复制影响不变式的成员

规定(Shall):拷贝构造函数必须复制所有影响类不变式的数据成员和基类子对象。

理由:确保拷贝后的对象保持与原对象相同的有效状态和不变式约束。

例外情况:不影响类不变式的成员(如缓存)无需复制(AV Rule 83);若类采用引用计数机制,则不必在所有情况下执行字面量拷贝。

AV Rule 77.1:禁止产生与隐式拷贝构造函数签名冲突的默认参数

规定(Shall):成员函数的定义不得包含使其签名与对应类/结构体的隐式声明拷贝构造函数相同的默认参数。

理由:此类歧义并非所有编译器都能诊断,应主动避免。

析构函数

AV Rule 78:含虚函数的基类必须定义虚析构函数

规定(Shall):所有包含虚函数的基类必须定义虚析构函数。

理由:防止未定义行为;若通过基类指针删除派生类对象时基类析构函数非虚,将导致派生类部分未被正确销毁。

备注:此规则不强制要求使用动态内存分配,仅针对多态删除的安全性(AV Rule 206)

AV Rule 79:析构函数负责释放类获取的所有资源

规定(Shall):类获取的所有资源必须由该类的析构函数负责释放。

理由:防止资源泄漏,特别是在异常或错误处理路径中确保资源能被自动且可靠地回收(RAII)。

赋值运算符

AV Rule 80:优先使用默认拷贝与赋值运算符

规定(Will):当默认的拷贝构造函数和赋值运算符能提供合理的语义时,应直接使用默认实现。

理由:默认生成的版本通常比手写代码更正确、更易维护且执行效率更高。

AV Rule 81:赋值运算符必须正确处理自赋值

规定(Shall):赋值运算符的实现必须能够正确处理自赋值(即 a = a)的情况。

AV Rule 82:赋值运算符返回 *this 引用

规定(Shall):赋值运算符必须返回对 *this 的引用。

理由:保持与标准库类型及内置类型的一致性,以支持链式赋值表达式(如 a = b = c)。

AV Rule 83:赋值运算符需赋值所有影响不变式的成员

规定(Shall):赋值运算符必须赋值所有影响类不变式的数据成员和基类子对象。

理由:确保赋值后目标对象维持有效的类不变式

备注:对于有状态的虚基类,需保证连续赋值的语义等价性以实现可移植的正确拷贝。

例外情况:不影响类不变式的成员(如缓存数据)无需在赋值操作中复制。

运算符重载

AV Rule 84:谨慎且符合惯例地使用运算符重载

规定(Will):运算符重载应谨慎使用,并严格遵循语言的自然语义和既定惯例。

理由:非惯例或不一致的运算符重载极易导致代码混淆;重载仅在能增强代码清晰度时使用,且必须保持语义一致性(例如 += 的含义必须与 + 和 = 的组合含义一致)。

AV Rule 85:成对定义互为反义的运算符

规定(Will):当两个运算符互为反义(如 == 与 !=)时,必须同时定义两者,且其中一个应基于另一个来实现。

理由:提供其中一个运算符时,用户合理预期另一个也存在;基于一个实现另一个可避免逻辑不一致并简化后续维护。

继承

AV Rule 86:使用具体类型表示简单独立概念

规定(Should):应使用具体类型来表示简单的、独立的概念。

理由:设计良好的具体类通常在空间和时间上高效,对其他类的依赖最小,且易于理解并可独立使用。

AV Rule 87:继承体系应基于抽象类构建

规定(Should):类的继承体系应当基于抽象类进行设计和构建。

理由:基于抽象类的层次结构有助于聚焦于清晰的接口设计,将实现细节隔离在接口之外,减少编译依赖,并允许替代实现共存。

AV Rule 88:严格限制多重继承的使用形式

规定(Shall):多重继承仅允许采用以下受限形式:n个接口 + m个私有实现 + 至多一个受保护实现。

理由:不受限的多重继承会导致复杂的继承层次结构,使其难以理解和维护。

AV Rule 88.1:显式声明有状态虚基类

规定(Shall):在访问有状态虚基类的每个派生类中,必须显式声明该虚基类。

理由:在层次结构的每一层(使用该基类处)显式声明有状态虚基类,可明确文档化一个事实:不能对虚基类中包含的数据的独占使用做任何假设。

AV Rule 89:禁止同一体系中基类兼具虚与非虚继承

规定(Shall):在同一继承体系中,同一个基类不得同时作为虚基类和非虚基类存在。

理由:这种混合使用会导致继承体系变得难以理解和使用。

AV Rule 90:高频接口应保持最小化、通用且抽象

规定(Should):被频繁使用的接口应当设计为最小化、通用且抽象的。

理由:使接口在面对其所属继承体系的变更时能够保持稳定。

AV Rule 91:公有继承仅用于实现“is-a”关系

规定(Will):公有继承必须且仅用于表达“is-a”(是一个)的语义关系。

理由:C++中公有继承与私有/保护继承含义截然不同。公有继承断言派生类是基类的子类型,即派生类对象可在任何需要基类对象的上下文中使用;而私有和保护继承仅表示“is-implemented-in-terms-of”,纯属实现技术,不涉及接口契约。混淆二者将导致语义错误和设计缺陷。

AV Rule 92:子类型必须遵循里氏替换原则

规定(Will):公有派生类(子类型)在多态赋值场景中,其重写方法的前置条件不得强于基类方法,后置条件不得弱于基类方法;即子类方法应“期望更少、交付更多”。

理由:确保派生类在基类上下文中使用时行为可预测,符合里氏替换原则(Liskov Substitution Principle)。

AV Rule 93:“has-a”或“is-implemented-in-terms-of”关系的建模方式

规定(Will):“has-a”或“is-implemented-in-terms-of”关系必须通过成员变量或非公有继承来实现。

理由:公有继承专用于表达“is-a”关系,而非公有继承和组合才正确表达实现依赖或包含关系,避免语义混淆。

AV Rule 94:禁止重定义继承的非虚函数

规定(Shall):不得在派生类中重新定义从基类继承的非虚函数。

理由:防止对象在不同上下文中表现出“两面性”行为,维护接口一致性。

AV Rule 95:禁止重定义继承的默认参数

规定(Shall):绝不允许重新定义从基类继承的默认参数值。

理由:对虚函数的默认参数进行重定义通常会导致令人意外的运行时行为,因为默认参数是静态绑定的。

AV Rule 96:禁止对数组进行多态处理

规定(Shall):不得将数组作为多态对象使用。

理由:C/C++中数组索引基于指针算术(a[i] 等价于 a + i * sizeof(element)),而派生类对象通常大于基类对象,导致指针算术与多态不兼容,引发内存访问错误。

AV Rule 97:接口中禁用原生数组

规定(Shall):接口中不得使用原生数组类型,应改用封装的Array类。

理由:原生数组作为参数传递时会退化为指针,丢失长度信息并引发“数组退化”问题,长期被视为错误根源。

AV Rule 97.1:禁止对虚成员函数指针进行相等性比较

规定(Shall):相等运算符(== 或 !=)的任一操作数均不得为指向虚成员函数的指针。

理由:根据 C++ 标准,若相等运算符的任一操作数为指向虚成员函数的指针,其比较结果是未指定的,会导致不可预测的行为。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:crackme安全实验室 crackme.net crackme.net《【第二部分】写出军规级代码:JSF AV C++编码标准》

评论:0   参与:  0