MISRA C 2012 标准浅析

2023-12-04 14:20
文章标签 2012 浅析 标准 misra

本文主要是介绍MISRA C 2012 标准浅析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

MISRA(The Motor Industry Software Reliability Association),汽车工业软件可靠性联会; 1994年,英国成立。致力于协助汽车厂商开发安全可靠的软件的跨国协会,其成员包括:AB汽车电子、罗孚汽车、宾利汽车、福特汽车、捷豹汽车、路虎公司、Lotus公司、MIRA公司、Ricardo公司、TRW汽车电子、利兹大学和福特VISTEON汽车系统公司。

MISRA支持C语言标准的发展历程:

MISRA C 2012目前已经修订了多次,最近版本是AMD2,已经支持C11标准。MISRA主要是功能安全,而CERT C/C++则更多的关注安全威胁。MISRA C 2012:AMD2总计规则数175条,其中规则158条,指令17条。其中指令基本上是无法被SAST工具所检测分析的。175条规则中强制为15条、必要规则120条、建议规则40条。

MISRA标准中每一条规则有很多描述信息,包括类别、可判断性、支持语言标准、分析范围、规则编号、规则描述等。请见下图示意:

对于这些规则描述项说明如下:

标准分类:规则(Rule)和指令(Directive)之间的区别:

指令是一种描述性的指导规范,它无法提供执行符合性检查所需的完整描述。为了能够进行检查,需要给评测人员提供额外的信息,如设计文件或需求说明。

指令部分主要分为:实现、编译与构建、要求追踪、代码设计四个部分,共16条规范。

规则可以对相关要求提供完整的描述,评测人员或静态分析工具可以在不需要额外信息的情况下检查源代码是否符合对应规则。

指令:仅仅依靠源代码分析,无法对指令进行合规性判定,往往需要开发人员提供更多的信息,如设计文档和要求说明。静态代码分析工具可以判断代码符合指令,但对于代码不符合指令的情况,代码分析工具给出的结果可能不一致。

规则:仅仅依靠源代码分析,就可以对规则进行判断,不需要开发人员提供更多的信息,所有的静态代码检测工具都应具有对规则进行合规性判定的能力。

Category类别:

Mandatory guidelines 强制的:2012版增加的

声明符合Misra的C代码应遵守所有强制性准则-不允许偏差

Required guidelines必须的:

公司或项目可以选择将任何必须的准则视为强制性准则,不符合需要正式的偏差许可

Advisory guidelines建议的:

建议性的,不符合应被记录,但是无需正式的偏差许可

Decidability可判定性和Undecidable 不可判定性:

Decidable可判断的,总是能够在任何程序中使用工具确定代码是否合规,否则就是不可判定的

例如:

可判定的,规则11.3:取决于源指针和目标指针的类型

不可判定的,规则12.2:取决于移位运算符右侧操作数的值。

Analysis Scope分析范围

Single translation unit rule 单一编译单元

通过隔离分析每个编译单元中的源代码,可以可靠地进行验证的代码

System rules系统级

只能通过分析整个系统中的源代码了完全检查验证

MISRA合规对于企业的价值:

  1. MISRA是汽车行业公认的C/C++语言编码规范;
  2. MISRA和AUTOSAR已经联合,即将发布C++最佳行业实践标准;
  3. 符合编码规范是ISO 26262:2018-6软件功能安全开发标准的内在要求之一,开发ASIL功能安全项目必须满足;
  4. 客户软件过程能力审核要求;
  5. 在研发生命周期早期发现软件中的缺陷,预防成本投入会大幅度降低投产后的售后维护成本。

对于C、C++语言开发,我们关注里面包括的运行时缺陷,例如缓冲区溢出、整数溢出、数组越界、内存泄露、空指针解引用等缺陷。但是对于出海的企业,例如车企其开发软件遵守MISRA标准是必要的。这些规则虽然大多数不会导致程序崩溃,但是会给这些软件运行带来潜在的出错风险,对于可能造成重大生命财产随时的软件还是应该去遵守的。下面我们列举几个例子、说明代码不安全性主要来源。

  1. 开发者编码引入的错误;

     例如M6-2-1:赋值运算符不得用于子表达式;  a = b = c = 10;    if((x=y) != 0)

  1. 开发者对C/C++语言的误解;

      例如:M5-0-2: 表达式中的 C++ 运算符优先规则应受到有限的依赖

     x = ( a + b );       x = static_cast< uint16_t > ( a ) + b; 

  1. 编译器不执行开发人员所期望的操作;

     例如:规则 M4-5-3:类型(普通)char 和 wchar_t 的表达式不得用作除赋值运算符 =、相等运算符 == 和 ! 之外的内置运算符的操作数 = 和一元 & 运算符。

     if ( ( ch >= ‘a’ ) && ( ch <= ‘z’ ) )

  1. 编译器包含错误;

    例如: M1-0-2: (强制性,工具链,非自动化)仅当多个编译器具有通用的定义接口时,才应使用多个编译器;

如果一个模块要用C++以外的语言实现,或者使用不同的编译器编译,需确保这个模块的正确集成。强制性考虑的问题包括:堆栈使用、参数传递、数据值的存储方式(长度、对齐、混叠、叠加等)。

  1. 运行时错误;

 例如A18-0-2: 应检查从字符串到数值的转换的错误状态:

std::uint16_t a;     std::cin >> a;    // 没有检测到错误

目前,业界对于MISRA C 2012:ADM2支持最好的SAST工具是Coverity,支持的规则最多,检测效果最好。其次是北大Cobot,检测效果还可以。下面是MISRA C 2012规则全集,请参考。

分类子类编号类别规则项支持启用
指令实施/实现Dir 1.1必要程序输出所依赖的任何由实现定义的行为都应被记录和理解
编译与构建Dir 2.1必要所有源文件都应通过编译且没有任何编译错误
需求可追溯性 Dir 3.1必要所有代码应可追溯到书面要求
代码设计Dir 4.1必要所有运行时缺陷都最小化
Dir 4.2建议汇编语言的所有运用都应记录在案
Dir 4.3必要汇编语言应被封装和隔离
Dir 4.4建议代码段不应被“注释掉”
Dir 4.5建议具有相同可见性的相同名称空间中的标识符在印刷/屏幕显示上应明确
Dir 4.6建议应使用指示大小和符号的 typedef 类型代替基本数字类型
Dir 4.7必要如果函数返回错误信息,则应测试该错误信息
Dir 4.8建议如果一个指向结构体或联合体的指针在编译/解释时从未被反引用,则应隐藏该对象的实现
Dir 4.9建议应该优先使用函数,而不是类似函数的宏(如果它们可以互换)
Dir 4.10必要应采取预防措施以防止头文件的内容被多次包含
Dir 4.11必要应该检查传递给库函数的值的有效性。
Dir 4.12必要不得使用动态内存分配
Dir 4.13建议用于对资源进行操作的功能应按适当的顺序调用
Dir 4.14必要应该检查从外部源接收的值的有效性。 
规则标准 C 环境Rule 1.1必要程序不得违反标准 C 语法和约束,并且不得超出具体实现的编译限制
Rule 1.2建议不应该使用语言扩展
Rule 1.3必要不得发生未定义或严重的未指定行为
Rule 1.4必要不得使用不成熟的语言特性
未使用的代码Rule 2.1必要项目不能包含不可达代码
Rule 2.2建议不得有无效代码(dead code)
Rule 2.3建议项目不应包含未被使用的类型(type)声明
Rule 2.4建议项目不应包含未被使用的类型标签(tag)声明
Rule 2.5建议项目不应包含未被使用的宏(macro)声明
Rule 2.6建议函数不应包含未被使用的执行标签(label)声明
Rule 2.7建议函数中不应有未使用的变量
注释Rule 3.1必要字符序列注释“/*”和“//”(&#47;*与&#47;&#47;)不应出现在注释中
Rule 3.2必要“//”注释中不得使用换行(即“//”注释中不得使用行拼接符“\”)
字符集和词汇约定Rule 4.1必要八进制和十六进制转译序列应有明确的终止识别标识
Rule 4.2建议禁止使用三字母词(trigraphs)
标识符Rule 5.1必要外部标识符不得重名
Rule 5.2必要同范围和命名空间内的标识符不得重名
Rule 5.3必要内部声明的标识符不得隐藏外部声明的标识符
Rule 5.4必要宏标识符不得重名
Rule 5.5必要宏标识符与其他标识符不得重名
Rule 5.6必要typedef 名称应是唯一标识符
Rule 5.7必要标签(tag)名称应是唯一标识符
Rule 5.8必要全局(external linkage)对象和函数的标识符应是唯一的
Rule 5.9建议局部全局(internal linkage)对象和函数的标识符应是唯一的
类型Rule 6.1必要位域(位带)仅允许使用适当的类型来声明(位域成员类型限制)
Rule 6.2必要单比特(single-bit)位域成员不可声明为有符号类型
字符和常量Rule 7.1必要禁止使用八进制常数
Rule 7.2必要后缀“u”或“U”应使用于所有无符号的整数常量
Rule 7.3必要小写字符“l”不得作为常量的后缀使用(仅可使用“L”)
Rule 7.4必要除非对象的类型为“指向 const char 的指针”,否则不得将字符串常量赋值给该对象
声明和定义Rule 8.1必要类型须明确声明
Rule 8.2必要函数类型应为带有命名形参的原型形式
Rule 8.3必要对象或函数的所有声明均应使用相同的名称和类型限定符
Rule 8.4必要全局(external linkage)的对象和函数,应有显式的合规的声明
Rule 8.5必要全局对象或函数应在且只在一个文件中声明一次
Rule 8.6必要全局标识符应在且只在一处定义
Rule 8.7建议仅在本编译单元中调用的对象和函数,应定义成局部属性
Rule 8.8必要“static”修饰符应用在所有局部全局对象和局部函数(internal linkage)的声明中
Rule 8.9建议若一个对象的标识符仅在一个函数中出现,则应将它定义在块范围内
Rule 8.10必要内联函数应使用静态存储类声明
Rule 8.11建议声明具有外部链接的数组时,应明确指定其大小
Rule 8.12必要在枚举列表中,隐式指定的枚举常量的值应唯一
Rule 8.13建议指针应尽可能指向 const 限定类型
Rule 8.14必要不得使用类型限定符“restrict”
初始化Rule 9.1强制具有自动存储持续时间的对象(临时变量)的值在设置前不得读取
Rule 9.2必要集合或联合体的初始化应括在花括号“{}”中
Rule 9.3必要数组不得部分初始化
Rule 9.4必要数组的元素不得被初始化超过一次
Rule 9.5必要在使用指定初始化方式初始化数组对象的情况下,应明确指定数组的大小
基本类型模型 Rule 10.1必要操作数不得为不适当的基本类型
Rule 10.2必要字符类型的表达式不得在加减运算中使用不当
Rule 10.3必要表达式的值不得赋值给具有较窄基本类型或不同基本类型的对象
Rule 10.4必要执行常规算术转换的运算符的两个操作数应有相同的基本类型
Rule 10.5建议表达式的值不应(强制)转换为不适当的基本类型
Rule 10.6必要复合表达式的值不得赋值给具有较宽基本类型的对象
Rule 10.7必要如果将复合表达式用作执行常规算术转换的运算符的一个操作数,则另一个操作数不得具有更宽的基本类型
Rule 10.8必要 复合表达式的值不得转换为其他基本类型或更宽的基本类型
指针类型转换Rule 11.1必要不得在指向函数的指针和任何其他类型的指针之间进行转换
Rule 11.2必要不得在指向不完整类型的指针和其他任何类型间进行转换
Rule 11.3必要不得在指向不同对象类型的指针之间执行强制转换
Rule 11.4建议不得在指向对象的指针和整数类型之间进行转换
Rule 11.5建议不得将指向 void 的指针转换为指向对象的指针
Rule 11.6必要不得在指向 void 的指针和算术类型之间执行强制转换
Rule 11.7必要不得在指向对象的指针和非整数算术类型之间执行强制转换
Rule 11.8必要强制转换不得从指针指向的类型中删除任何 const 或 volatile 限定符
Rule 11.9必要宏“NULL”是整数型空指针常量的唯一允许形式
表达式Rule 12.1建议表达式中运算符的优先级应明确
Rule 12.2必要移位运算符的右操作数应在零到比左操作数基本类型的位宽度小一的范围内
Rule 12.3建议不得使用逗号(,)运算符
Rule 12.4建议常量表达式的求值不应导致无符号整数的回绕
Rule 12.5建议sizeof运算符不能有声明为“类型数组”的函数参数作为操作数
副作用Rule 13.1必要初始化程序列表不得包含持久性副作用
Rule 13.2必要在所有允许的评估顺序中表达式的值及其持久的其他作用应该保持相同。
Rule 13.3建议包含自增(++)或自减(--)运算符的完整表达式,除由自增或自减运算符引起的副作用外,不应有其他潜在的副作用
Rule 13.4建议不得使用赋值运算符的结果
Rule 13.5必要逻辑与(&&)和逻辑或(||)的右操作数不得含有持久性副作用
Rule 13.6强制sizeof 运算符的操作数不得包含任何可能产生副作用的表达
控制语句表达式Rule 14.1必要循环计数器的基本类型不能为浮点型
Rule 14.2必要 for 循环应为良好格式
Rule 14.3必要控制表达式不得是值不变的
Rule 14.4必要if 语句和循环语句的控制表达式的基本类型应为布尔型
控制流Rule 15.1建议不应使用 goto 语句
Rule 15.2必要goto 语句仅允许跳到在同一函数中声明的稍后位置的标签
Rule 15.3必要goto语句引用的标签必须在goto语句所在代码块或包含该代码块的上级代码块中声明
Rule 15.4建议最多只能有一个用于终止循环语句的 break 或 goto 语句
Rule 15.5建议应仅在函数的末尾有单个函数出口
Rule 15.6必要循环语句和选择语句的主体应为复合语句
Rule 15.7必要所有的 if…else if 构造都应以 else 语句结束
Switch 语句Rule 16.1必要switch 语句应格式正确
Rule 16.2必要switch 标签只能出现在构成 switch 语句主体的复合语句的最外层
Rule 16.3必要每一个 switch 子句(switch-clause)都应以无条件 break 语句终止
Rule 16.4必要每个 switch 语句都应具有 default 标签
Rule 16.5必要Default 标签应作为 switch 语句的第一个或最后一个 switch 标签
Rule 16.6必要每个 switch 语句应至少有两个 switch 子句
Rule 16.7必要switch 语句的控制表达式(switch-expression)的基本类型不得是布尔型
函数Rule 17.1必要不得使用<stdarg.h>的功能
Rule 17.2必要函数不得直接或间接调用自身(不得使用递归函数)
Rule 17.3强制禁止隐式声明函数
Rule 17.4强制具有非 void 返回类型的函数的所有退出路径都应为具有带有表达式的显式return 语句
Rule 17.5建议与数组型函数形参对应的函数入参应具有适当数量的元素
Rule 17.6强制数组形参的声明不得在[]之间包含 static 关键字
Rule 17.7必要非 void 返回类型的函数的返回值应该被使用
Rule 17.8建议不应更改函数形参
指针和数组Rule 18.1必要指针操作数的算术运算应仅用于寻址与该指针操作数相同数组的元素
Rule 18.2必要指针之间的减法应仅用于寻址同一数组元素的指针
Rule 18.3必要关系运算符>,> =,<和<=不得应用于指针类型的对象,除非它们指向同一对象
Rule 18.4建议“+,-,+=”和“-=”运算符不得应用于指针类型的表达式
Rule 18.5建议声明中最多包含两层指针嵌套
Rule 18.6必要具有自动存储功能的对象的地址不得复制给在它的生命周期结束后仍会存在的另一个对象
Rule 18.7必要不得声明可变的数组成员
Rule 18.8必要不得使用可变长数组类型
重叠存储Rule 19.1强制不得将对象赋值或复制给重叠的对象
Rule 19.2必要不得使用 union 关键字
预处理指令Rule 20.1建议#include 指令之前仅允许出现预处理指令或注释
Rule 20.2必要头文件名中不得出现“'”、“"”、“\”、字符以及“/*”或“//”字符序列
Rule 20.3必要#include 指令后须跟随<filename>或"filename"序列
Rule 20.4必要宏不得与关键字同名
Rule 20.5建议不应使用#undef
Rule 20.6必要看起来像预处理指令的符号不得出现在宏参数内
Rule 20.7必要宏参数展开产生的表达式应放在括号内
Rule 20.8必要#if 或#elif 预处理指令的控制表达式的计算结果应为 0 或 1
Rule 20.9必要#if 或#elif 预处理指令的控制表达式中使用的所有标识符应在其评估前被#define 定义
Rule 20.10建议不应使用“#”和“##”预处理运算符
Rule 20.11必要紧跟在“#”运算符之后的宏参数后面不得紧随“##”运算符
Rule 20.12必要用作“#”或“##”运算符的操作数的宏参数,不得是本身需要进一步宏替换的操作数
Rule 20.13必要以“#”作为第一个字符的一行代码应为有效的预处理指令
Rule 20.14必要所有#else,#elif 和#endif 预处理程序指令都应和与其相关的#if,#ifdef 或#ifndef 指令位于同一文件中
标准库Rule 21.1必要不得将#define 和#undef 用于保留的标识符或保留的宏名称
Rule 21.2必要不得声明保留的标识符或宏名称
Rule 21.3必要不得使用<stdlib.h>中的内存分配和释放函数
Rule 21.4必要不得使用标准头文件<setjmp.h>
Rule 21.5必要不得使用标准头文件<signal.h>
Rule 21.6必要不得使用标准库输入/输出函数
Rule 21.7必要不得使用<stdlib.h>中的 atof、atoi、atol 和 atoll 函数
Rule 21.8必要不得使用<stdlib.h>中的 abort, exit, getenv 和 system 函数
Rule 21.9必要不得使用<stdlib.h>中的 bsearch 和 qsort 函数
Rule 21.10必要不得使用标准库时间和日期功能
Rule 21.11必要不得使用标准头文件<tgmath.h>
Rule 21.12建议不得使用<fenv.h>的异常处理功能
Rule 21.13强制在 <ctype.h> 中传递给函数的任何值都应表示为 unsigned char或者值 EOF。
Rule 21.14必要不应使用标准库函数 memcmp 与以 null 终止的字符串进行比 较。
Rule 21.15必要标准库函数 memcpy、memmove 和 memcmp 的指针参数应该是指向兼容类型的合格或不合格版本的指针。
Rule 21.16必要标准库函数 memcmp 的指针参数应该指向指针类型、本质上signed 类型、本质上 unsigned 类型、本质上 Boolean 类型或者本质上 enum类型。
Rule 21.17强制使用来自 <string.h> 中的字符串处理函数不应导致访问超出它们的指针参数引用的对象的范围。
Rule 21.18强制传递给 <string.h> 中的任何函数的 size_t 参数都应有一个适当的值。
Rule 21.19强制只应将标准库函数 localeconv、getenv、setlocale 或 strerror返回的指针用作它们好像具有指向 const 限定类型的指针。
Rule 21.20强制标准库函数 asctime、ctime、gmtime、localtime、localeconv、getenv、setlocale 或 strerror 返回的指针不应后接调用相同函数的后续调用。
Rule 21.21必要不应使用 <stdlib.h> 的标准库函数 system。 
资源Rule 22.1必要通过标准库功能动态获取的所有资源均应明确释放
Rule 22.2强制只有通过标准库函数分配的内存块才能释放
Rule 22.3必要不得在不同的数据流上同时打开同一文件以进行读写访问
Rule 22.4强制禁止尝试对以只读方式打开的流执行写操作
Rule 22.5强制禁止反引用指向 FILE 对象的指针
Rule 22.6强制关联的流关闭后,禁止再使用指向 FILE 的指针值
Rule 22.7必要宏 EOF 只应与能够返回 EOF 的任何标准库函数的未修改返回值进行比较。
Rule 22.8必要在调用 errno-setting-function 之前,应将 errno 的值设置为零。
Rule 22.9必要在调用 errno-setting-function 之后,应测试 errno 的值是否为零。
Rule 22.10必要仅当要调用的最后一个函数为 errno-setting-function 时,才应测试 errno 的值。

(结束)

这篇关于MISRA C 2012 标准浅析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/453782

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

数据治理框架-ISO数据治理标准

引言 "数据治理"并不是一个新的概念,国内外有很多组织专注于数据治理理论和实践的研究。目前国际上,主要的数据治理框架有ISO数据治理标准、GDI数据治理框架、DAMA数据治理管理框架等。 ISO数据治理标准 改标准阐述了数据治理的标准、基本原则和数据治理模型,是一套完整的数据治理方法论。 ISO/IEC 38505标准的数据治理方法论的核心内容如下: 数据治理的目标:促进组织高效、合理地

C 标准库 - `<float.h>`

C 标准库 - <float.h> 概述 <float.h> 是 C 标准库中的一个头文件,它定义了与浮点数类型相关的宏。这些宏提供了关于浮点数的属性信息,如精度、最小和最大值、以及舍入误差等。这个头文件对于需要精确控制浮点数行为的程序非常有用,尤其是在数值计算和科学计算领域。 主要宏 <float.h> 中定义了许多宏,下面列举了一些主要的宏: FLT_RADIX:定义了浮点数的基数。

(入门篇)JavaScript 网页设计案例浅析-简单的交互式图片轮播

网页设计已经成为了每个前端开发者的必备技能,而 JavaScript 作为前端三大基础之一,更是为网页赋予了互动性和动态效果。本篇文章将通过一个简单的 JavaScript 案例,带你了解网页设计中的一些常见技巧和技术原理。今天就说一说一个常见的图片轮播效果。相信大家在各类电商网站、个人博客或者展示页面中,都看到过这种轮播图。它的核心功能是展示多张图片,并且用户可以通过点击按钮,左右切换图片。

《C++标准库》读书笔记/第一天(C++新特性(1))

C++11新特性(1) 以auto完成类型自动推导 auto i=42; //以auto声明的变量,其类型会根据其初值被自动推倒出来,因此一定需要一个初始化操作; static auto a=0.19;//可以用额外限定符修饰 vector<string> v;  auto pos=v.begin();//如果类型很长或类型表达式复杂 auto很有用; auto l=[] (int

标准IO与系统IO

概念区别 标准IO:(libc提供) fopen fread fwrite 系统IO:(linux系统提供) open read write 操作效率 因为内存与磁盘的执行效率不同 系统IO: 把数据从内存直接写到磁盘上 标准IO: 数据写到缓存,再刷写到磁盘上

通信工程学习:什么是AM标准调幅

AM标准调幅       AM标准调幅,即Amplitude Modulation(振幅调制),是一种在电子通信中广泛使用的调制方法,特别是在无线电载波传输信息方面。以下是关于AM标准调幅的详细解释: 一、AM标准调幅的定义与原理 AM标准调幅的定义:        AM调幅是通过改变载波信号的振幅(即信号强度或电压幅度),使其与信息信号(如音频、视频等)同步变化,从而实现信息的传

【电子通识】洁净度等级划分及等级标准

洁净度常用于评估半导体、生物制药、医疗、实验室及科研院所、新能源等领域的洁净室、无尘室或者无菌室等环境。         一般来说,晶圆光刻、制造、测试等级为100级或1000级的洁净间,百级洁净间要求空气中0.5微米的尘埃粒子数不得超过每立方米3520个;等级为1000级的洁净间要求0.5微米的尘埃粒子数不得超过每立方米35200个。         晶圆切割或封装工序一

标准库标头 <filesystem> (C++17)学习

此头文件是文件系统支持库的一部分。本篇介绍filesystem命名空间的一些函数。 函数 在命名空间 std::filesystem 定义 absolute (C++17) 组成一个绝对路径 (函数) canonicalweakly_canonical (C++17) 组成一个规范路径 (函数) relativeproximate (C++17) 组成一个相对路径 (函数) copy (C

风暴项目个性化推荐系统浅析

风暴项目的主要任务是搭建自媒体平台,作为主开发人员的我希望把工作重心放在个性化推荐系统上。 目前风暴项目的个性化推荐是基于用户行为信息记录实现的,也就是说对于每条资讯,数据库中有字段标明其类型。建立一张用户浏览表,对用户的浏览行为进行记录,从中可以获取当前用户对哪类资讯感兴趣。 若用户第一次登陆,则按默认规则选取热点资讯做推荐,及所有资讯按浏览量降序排序,取前4个。另外,我考虑到后期可能有商业