C/C++ 编程规范总结

news2024/11/28 16:28:19

目录

前言

一、编程规范的作用

二、规范的三种形式

三、规范的内容

1. 基本原则

原则1-1

原则1-2

原则1-3

原则1-4

原则1-5

原则1-6

原则1-7

2. 布局

规则2-1-1

规则2-1-2

规则2-1-3

规则2-1-4

规则2-1-5

规则2-1-6

规则2-2-1

规则2-2-2

规则2-2-3

建议2-3-1

规则2-4-1

规则2-4-2

规则2-4-3

规则2-4-4

规则2-4-2

规则2-4-6

规则2-5-1

3. 注释

规则3-1

规则3-2

规则3-3

​编辑

规则3-4

4. 命名规则

规则4-1

规则4-2

规则4-3

规则4-4

规则4-5

规则4-6

规则4-7

规则4-8

规则4-9

规则4-10

规则4-11

建议4-12

建议4-13

5. 变量、常量与类型

规则5-1

规则5-2

建议5-3

6. 表达式与语句

规则6-1

规则6-2

规则6-3

规则6-4

规则6-5        

​编辑

规则6-6

​编辑

规则6-7

规则6-8

规则6-9

建议6-10

7. 函数与过程

规则7-1

规则7-2

规则7-3

规则7-4

规则7-5

建议7-6

建议7-7

建议7-8

建议7-9

8. 可靠性

规则8-1

规则8-2

规则8-3

规则8-4

规则8-5

规则8-6

规则8-7

规则8-8

建议8-9

9. 可测试性

规则9-1

规则9-2

规则9-3

10.断言与错误外理

规则10-1

规则10-2

规则10-3


前言

编码规范是成为一个优质程序员的重要一课,它是编程的样式的模板。

为了跟大佬们一样写出简洁、可维护、可靠、可测试、高效、可移植的代码,我参考网上的资料做了一个归类。

一、编程规范的作用

1.提高源程序的可读性和可维护性

2.降低错误的机会

3.提高源代码可重用性和质量

二、规范的三种形式

1.原则:编程时应该坚持的指导思想

2.规则:编程时必须遵守的约定

3.建议: 编程时必须加以考虑的约定

三、规范的内容

1. 基本原则

原则1-1

首先是为人编写程序,其次才是计算机。
说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发、测试、生产、用户使用、版本升级和后期维护等长期过程,只有易读、易维护的软件代码才具有生命力。

原则1-2

保持代码的简明清晰,避免过分的编程技巧。
简单是最美。保持代码的简单化是软件工程化的基本要求。不要过分追求技巧,否则会降低程序的可读性。

原则1-3

所有的代码尽量遵循ANSI C标准
所有的代码尽可能遵循ANSI C标准,尽可能不使用ANSI C未定义的或编译器扩展的功能。

原则1-4

编程时首先达到正确性,其次考虑效率。
编程首先考虑的是满足正确性、健壮性、可维护性、可移植性等质量因素,最后才考虑程序的效率和资源占用。

原则1-5

避免或少用全局变量
过多地使用全局变量,会导致模块间的紧耦合违反模块化的要求

原则1-6

尽量遥免使用GOTO语句

原则1-7

尽可能复用、修正老的代码。
尽量选择可借用的代码,对其修改优化以达到自身要求。

2. 布局

规则2-1-1

遵循统一的布局顺序来书写头文件#ifmndef 文件名 H(全大写)
#defie
文件名 H
其它条件编译选项
#include(依次为标准库头文件、非标准库头文件)常量定义
全局宏
全局数据类型
类定义
模板 (template)(包括C++中的类模板和函数模板) 全局函数原型
#endif

规则2-1-2

遵循统一的布局顺序来书写实现文件
文件头注释
#include(依次为标准库头文件、非标准库头文件)常量定义
文件内部使用的宏
文件内部使用的数据类型
全局变量
本地变量(即静态全局变量)
局部函数原型
类的实现全局函数局部函数

规则2-1-3

使用注释块分离上面定义的节

规则2-1-4

头文件必须要避免重复包含

规则2-1-5

包含标准库头文件用尖括号 < >

包含非标准库头文件用双引号 " "

规则2-1-6

遵循统一的顺序书写类的定义及实现

类的定义(在定义文件中) 按如下顺序书写:

  • 公有属性,公有函数
  • 保护属性,保护函数
  • 私有属性,私有函数

类的实现(在实现文件中) 按如下顺序书写:

  • 构造函数,析构函数
  • 公有函数
  • 保护函数
  • 私有函数

规则2-2-1

程序中一行的代码和注释不能超过80列
包括空格在内不超过80列

规则2-2-2

if、 else、 else if、 for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{ }

规则2-2-3

结构型的数组、多维的数组如果在定义时初始化,按照数组的矩阵结构分行书写。

建议2-3-1

在switch语句中,每一个case分支和default要用{ }括起来,{ }中的内容需要缩进。

规则2-4-1

不同逻辑程序块之间要使用空行分隔

规则2-4-2

多元运算符和它们的操作数之间至少需要一个空格。

规则2-4-3

关键字之后要留空格

if、for、while等关键字之后应留一个空格再跟左括号 ’(’,以突出关键字

另外 sizeof 是关键字,strlen 是函数

规则2-4-4

函数名之后不要留空格

函数名后紧跟左括号 ’(’,以与关键字区别

规则2-4-2

不是行结束符号时其后要留空格

规则2-4-6

注释符与注释内容之间要用一个空格进行分隔

规则2-5-1

函数声明时,类型与名称不允许分行书写

3. 注释

规则3-1

文件头部必须进行注释,包括:.h文件、.c文件、.cpp文件、.inc文件、.def文件、编译说明文件.cfg等。
注释必须列出:版权信息、文件标识、内容摘要、版本号、作者、完成日期、修改信息等

规则3-2

函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、访问和修改的表、修改信息等。
说明:注释必须列出:函数名称、功能描述、输入参数、输出参数、返回值、修改信息等

规则3-3

包含在中代码块的结束处应加注释,便于阅读。

特别是多分支、多重嵌套的条件语句或循环语句。

规则3-4

注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释) 相邻位置不可放在下面,如放于上方则需与其上面的代码用空行隔开


4. 命名规则

规则4-1

标识符要采用英文单词或其组合,便于记忆和阅读,切总使用汉语拼音来命名。

标识符应当直观且可以拼读,可望文知义,避免使人产生误解。程序中的英文单词一般不要太复杂,用词应当准确。

规则4-2

标识符的命名应当符合“min-lengt && max-information”原则
较短的单词可通过去掉“元音”形成缩写,较长的单词可取单词的头几个字母形成缩写,一些单词有大家公认的缩写,常用单词的缩写必须统一。协议中的单词的缩写与协议保持一致对于某个系统使用的专用缩写应该在某处做统一说明。

例如:

  • temp 可缩写为 tmp ;
  • flag 可缩写为 flg ;
  • statistic 可缩写为 stat ;
  • increment 可缩写为 inc ;
  • message 可缩写为 msg ;

规则4-3

用正确的反义词组命名具有互斥意义的变量或相反动作的函数等

  • add / remove;
  • begin / end;
  • create / destroy;
  • insert / delete;
  • first / last;
  • get / release;
  • increment / decrementputget;
  • lock / unlock ;
  • open / close ;
  • min / max;
  • old / new;
  • start / stop;
  • source / target ;
  • next / previous;
  • show / hide;
  • send / receive ;
  • source / destination;
  • cut / paste;
  • up / down;

规则4-4

宏、常量名都要使用大写字母,用下划线 '_' 分割单词。

预编译宏定义使用下划线 '_' 开始。

例如:

DISP_BUF_SIZE、MIN_VALUE、MAX_VALUE 

规则4-5

变量名长度应小于31个字符,以保持与ANSI C标准一致。

不得取单个字符(如i、j、k等)作为变量名,但是局部循环变量除外。

规则4-6

程序中局部变量不要与全局变量重名
尽管局部变量和全局变量的作用域不同而不会发生语法错误,但容易使人误解。

规则4-7

使用一致的前缀来区分变量的作用域
变量活动范围前缀规范如下:

规则4-8

使用一致的小写类型指示符作为前缀来区分变量的类型。

规则4-9

完整的变量名应由前缀+变量名主体组成,变量名的主体应当使用“名词”或者“形容词 + 名词”,且首字母必须大写。

例如:

  • float g fValue; // 类型为浮点数的全局变量
  • char *pcOldChar; // 类型为字符指针的局部变量char

规则4-10

  • 结构名、联合名、枚举名由前缀T_开头。
  • 事件名由前缀EV 开头

规则4-11

类名采用大小写结合的方法。在构成类名的单词之间不用下划线,类名在开头加上C,类的成员变量统一在前面加m_前缀

建议4-12

尽量避免名字中出现数字编号,如Value1、Value2等,除非逻辑上的确需要编号

建议4-13

标识符前最好不加项目、产品、部门的标识
这样做的目的是为了代码的可重用性


5. 变量、常量与类型

规则5-1

宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来。
宏定义中,对表达式和变量使用括号,可以避免可能发生的计算错误

正例:
#define HANDLE(A,B) (( A ) / ( B ))

反例:
#define HANDLE(A,B)  (A / B)

规则5-2

使用宏定义多行语句时,必须使用把这些语句括起来。
在宏定义中,对多行语句使用大括号,可以避免可能发生的错误。

建议5-3

结构中元素的个数应适中。

若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数


6. 表达式与语句

规则6-1

一条语句只完成一个功能

规则6-2

在表达式中使用括号,使表达式的运算顺序更清晰。
由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,即使不加括号时运算顺序不会改变,也应当用括号确定表达式的操作顺序。

规则6-3

避免表达式中的附加功能,不要编写太复杂的复合表达式。

规则6-4

不可将布尔变量和逻辑表达式直接与TRUEFALSE或者1、0进行比较。TURE和FALSE的定义值是和语言环境相关的,且可能会被重定义的

规则6-5        

在条件判断语句中,当整型变量与0比较时,不可模仿布尔变量的风格,应当将整型变量用“==”或“!=”直接与0比较。

规则6-6

不可将浮点变量用“==”或“!=”上任何数字比较

规则6-7

应当将指针变量用“==”或“!=”与NULL比较

规则6-8

在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。

规则6-9

不可在for 循环体内修改循环变量防止for 循环失去控制。

建议6-10

循环嵌套次数不大于3次


7. 函数与过程

规则7-1

如果函数没有参数,则用void填充

规则7-2

如果参数是指针,且仅作输入用,则应在类型前加const。
防止该指针在函数体内被意外修改

例如:

int GetStrLen(const char *pcString):

规则7-3

不要省略返回值的类型,如果函数没有返回值,那么应声明为void类型

  • C语言中,凡不加类型说明的函数,一律自动按整型处理。如果不注明类型,容易被误解为void类型,产生不必要的麻烦
  • C++语言有很严格的类型安全检查,不允许上述情况发生。由于C++程序可以调用C函数,为了避免混乱,规定任何C/ C++函数都必须有类型。

规则7-4

对于有返回值的函数,每一个分支都必须有返回值。
为了保证对被调用函数返回值的判断,有返回值的函数中的每一个退出点都需要有返回值。

规则7-5

防止将函数的参数作为工作变量
将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。

建议7-6

如果返回值表示函数运行是否正常规定0为正常退出,不同非0值标识不同异常退出。避免使用TRUE或FALSE作为返回值

建议7-7

函数体的规模不能太大,尽量控制在200行代码之内

建议7-8

减少函数本身或函数间的递归调用
递归调用特别是函数间的递归调用 (如A->B->C->A),影响程序的可理解性,递归调用一般都占用较多的系统资源(如栈空间) ;递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。
对于前台软件为了系统的稳定性和可靠性,往往规定了进程的堆栈大小。如果采用了递归算法,收敛的条件又往往难以确定,很容易使得进程的堆栈溢出,破坏系统的正常运行:另外,由于无法确定递归的次数,降低了系统的稳定性和可靠性。

建议7-9

设计高扇入、合理扇出的函数
扇出是指一个函数直接调用(控制) 其它函数的数目,而扇入是指有多少上级函数调用它。
扇出过大,表明函数过分复杂,需要控制和协调过多的下级函数:而扇出过小,如总是1,表明函数的调用层次可能过多,这样不利于程序阅读和函数结构的分析,并且程序运行时会对系统资源如堆栈空间等造成压力。函数较合理的扇出(调度函数除外) 通常是3-5。扇出太大,一般是由于缺乏中间层次,可适当增加中间层次的函数。扇出太小,可把下级函数进步分解成多个函数,或合并到上级函数中。当然分解或合并函数时,不能改变要实现的功能,也不能违背函数间的独立性。

扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。


8. 可靠性

规则8-1

在程序编制之前,必须了解编译系统的内存分配方式,特别是编译系统对不同类型的变量的内存分配规则,如局部变量在何处分配、静态变量在何处分配等。

规则8-2

防止内存操作越界
内存操作主要是指对数组、指针、内存地址等的操作,内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细。

规则8-3

必须对动态申请的内存做有效性检查,并进行初始化;动态内存的释放必须和分配成对以防止内存泄漏,释放后内存指针置为NULL。

  • 对嵌入式系统,通常内存是有限的,内存的申请可能会失败,如果不检查就对该指针进行操作,可能出现异常,而且这种异常不是每次都出现比较难定位。
  • 指针释放后,该指针可能还是指向原有的内存块,可能不是,变成个野指针,一般用户不会对它再操作,但用户失误情况下对它的操作可能导致程序崩溃。

规则8-4

不使用realloc()。
调用realloc对一个内存块进行扩展,导致原来的内容发生了存储位置的变化, realloc函数既要调用free,又要调用malloc。执行时究竟调用哪个函数,取决于是要缩小还是扩大相应内存块的大小

规则8-5

变量在使用前应初始化,防止未经初始化的变量被引用。
不同的编译系统,定义的变量在初始化前其值是不确定的。有些系统会初始化为0,而有些不是

规则8-6

指针类型变量必须初始化为NULL

规则8-7

指针不要进行复杂的逻辑或算术操作。
指针加一的偏移,通常由指针的类型确定,如果通过复杂的逻辑或算术操作,则指针的位置就很难确定

规则8-8

如果指针类型明确不会改变,应该强制为const类型的指针,以加强编译器的检查
可以防止不必要的类型转换错误。

建议8-9

C++程序中,分配内存使用new和delete,而不使用malloc和free。
new和delete操作由编译器决定具体分配和释放内存的大小,相对于malloc和firee更为高级


9. 可测试性

规则9-1

在同一项目组或产品组内,为准备集成测试和系统联调,要有一套统一的调测开关及相应信息输出函数,并且要有详细的说明统一的调试接口和输出函数由模块设计和测试人员根据项目特性统一制订,由项目系统人员统一纳入系统设计中

规则9-2

在同一个项目组或产品组内,调测打印出的信息串要有统一的格式。信息串中应当包含所在的模块名(或源文件名) 及行号等信息。

规则9-3

在编写代码之前,应预先设计好程序调试与测试的方法和手段,并设计好各种调测开关及相应测试代码(如打印函数等)
程序的调试与测试是软件生存周期中非常重要的一个阶段,如何对软件进行较全面、高效率的测试并尽可能地找出软件中的错误就成为非常关键的问题。因此在编写源代码之前,除了要有一套比较完善的测试计划外,还应设计出一系列测试代码作为手段,为单元测试、集成测试及系统联调提供方便

10.断言与错误外理

规则10-1

整个软件系统应该采用统一的断言。如果系统不提供断言,则应该自己构造一个统一的断言供编程时使用。

规则10-2

指向指针的指针及更多级的指针必须逐级检查

规则10-3

正式软件产品中应把断言及其它调测代码去掉 (即把有关的调测开关关掉 ) ,加快软件运行速度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1300246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[ 蓝桥杯Web真题 ]-布局切换

目录 介绍 准备 目标 规定 思路 解法参考 介绍 经常用手机购物的同学或许见过这种功能&#xff0c;在浏览商品列表的时候&#xff0c;我们通过点击一个小小的按钮图标&#xff0c;就能快速将数据列表在大图&#xff08;通常是两列&#xff09;和列表两种布局间来回切换。…

HarmonyOS鸿蒙应用开发——HTTP网络访问与封装

文章目录 基本使用封装参考 基本使用 鸿蒙应用发起HTTP请求的基本使用&#xff0c;如下&#xff1a; 导入http模块创建httpRequest对象发起http请求&#xff0c;并处理响应结果 第一、导入http模块&#xff1a; import http from ohos.net.http第二、创建httpRequest对象&a…

本地搭建Linux DataEase数据可视化分析工具并实现公网访问

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务…

排序算法之七:归并排序(递归)

基本思想 基本思想&#xff1a; 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1…

初学vue3与ts:vue3选项式api获取当前路由地址

vue2的获取方法 this.$route.pathvue3选项式api获取方法 import { useRouter } from vue-router; const router useRouter(); console.log(router) console.log(router.currentRoute.value.path)

基于Springboot+Vue前后端分离的电影推荐系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

打补丁,生成.diff文件

作者&#xff1a;爱塔居 文章目录 目录 前言 步骤 一、在根目录上&#xff0c;输入添加指令 二、输入修改内容指令 三、生成补丁 前言 自己的理解&#xff0c;仅供参考&#xff0c;欢迎指正。 补丁的话&#xff0c;在我看来就是方便评审&#xff0c;更方便看修改代码吧。 步骤…

kafka中消息key作用与分区规则关系

在 kafka 2.0.0 的 java sdk 中 <dependency><groupId>org.apache.kafka</groupId><artifactId>kafka_2.12</artifactId><version>2.0.0</version> </dependency> ProducerRecord 中类注释如下 A key/value pair to be sen…

PCIe MPS参数介绍及如何更改

目录 1.简介 2.主要功能作用 3.MPS控制策略 4.如何更改 1.简介 MPS 该参数含义是一个TLP包里携带的有效净荷的最大值是多少字节&#xff08;该限制条件同时适用于写操作和读操作&#xff09;。 MRRS 该参数含义是一个TLP读请求包&#xff0c;一次最多能向接收端请求读出…

我有才打造专属个人或企业知识付费平台,核心功能设计

在当今信息爆炸的时代&#xff0c;知识管理已经成为了每个人必须面对的问题。然而&#xff0c;市面上的知识付费平台大多数都是通用的&#xff0c;无法满足个性化需求。 因此&#xff0c;我有才提供了一款专属定制的适合个人的知识付费平台。核心产品能力如下&#xff1a; 一…

【LeetCode】2723. 两个 Promise 对象相加

两个 Promise 对象相加 题目题解 题目 给定两个 promise 对象 promise1 和 promise2&#xff0c;返回一个新的 promise。promise1 和 promise2 都会被解析为一个数字。返回的 Promise 应该解析为这两个数字的和。 示例 1&#xff1a; 输入&#xff1a; promise1 new Promise…

Jmeter测试实践:文件下载接口

一 Jmeter步骤 1.打开jmeter4.0&#xff0c;新建测试计划&#xff0c;添加线程组。根据实际情况配置线程属性。 2.添加HTTP请求。根据接口文档进行配置。 Basic部分修改如下&#xff0c;Advanced部分保持默认。这里的参数id是文件的id&#xff0c;我进行了参数化&#xff0c…

Android : Room 数据库的基本用法 —简单应用

1.Room介绍&#xff1a; Android Room 是 Android 官方提供的一个持久性库&#xff0c;用于在 Android 应用程序中管理数据库。它提供了一个简单的 API 层&#xff0c;使得使用 SQLite 数据库变得更加容易和方便。 以下是 Android Room 的主要特点&#xff1a; 对象关系映射…

一键转换,轻松搞定!在线PDF转换网站让你的工作更高效!

在现代数字化时代&#xff0c;PDF文件已经成为了我们日常工作和生活中不可或缺的一部分。然而&#xff0c;有时候我们可能会遇到一些与PDF文件相关的问题&#xff0c;比如格式不兼容、无法编辑或转换等。这些问题可能会给我们带来一些困扰和烦恼。 你是否曾经因为PDF文件的格式…

翻译: 生成式人工智能的经济潜力 第3部分工作和生产力的影响 The economic potential of generative AI

麦肯锡报告 翻译: 生成式人工智能的经济潜力 第一部分商业价值 The economic potential of generative AI翻译: 生成式人工智能的经济潜力 第2部分行业影响 The economic potential of generative AI 1. 工作和生产力的影响 技术几十年来一直在改变工作的解剖学。多年来&…

Java JMM

JMM 全称: Java Memory Model (Java 内存模式)。 它是一种虚拟机规范, 用于屏蔽掉各种硬件和操作系统的内存访问差异, 以实现 Java 程序在各种平台下都能达到一致的并发效果。 主要规定了以下两点 一个线程如何以及何时可以看到其他线程修改过后的共享变量的值, 即线程之间共享…

最新Redis7持久化(权威出版)

首先我们要知道什么是持久化&#xff1a;持久化是指将数据保存到磁盘上&#xff0c;以确保在Redis服务器重启时数据不会丢失。 Redis支持两种主要的持久化方式&#xff1a;RDB持久化和AOF持久化 下面让我依次给你介绍一下&#xff1a; RDB持久化 作用 这是将Redis数据保存…

AttributeError: ‘bool‘ object has no attribute ‘sum‘

AttributeError: ‘bool’ object has no attribute ‘sum’ AttributeError: ‘bool’ object has no attribute ‘sum’ 解决方法 将torch.max(&#xff09;改为torch.argmax&#xff08;&#xff09;查看output和targets的数据类型是否都为tensor 以上就是全部内容&#…

Java基础50题:14. 使用方法求最大值(2种方法)

概述 使用方法求最大值。 创建方法求两个数的最大值max2&#xff0c;随后再写一个求3个数的最大值函数max3。 要求&#xff1a; 在max3这个方法中&#xff0c;调用max2函数&#xff0c;来实现3个数的最大值计算。 方法一 【代码】 public class P14 {public static int max…

【数据结构 — 排序 — 选择排序】

数据结构 — 排序 — 选择排序 一.选择排序1.基本思想2.直接选择排序2.1算法讲解2.2.代码实现2.2.1.函数定义2.2.2.算法接口实现2.2.3.测试代码实现2.2.4.测试展示 3.堆排序3.1.算法讲解3.2.代码实现3.2.1.函数定义3.2.2.算法接口实现3.2.3.测试代码实现3.2.4.测试展示 一.选择…