当前位置:   article > 正文

第七十七篇:车辆安全-车载软件C++语言开发指南(AUTOSAR C++)_c++适合做车载吗

c++适合做车载吗

C++是面向对象的编程,比C语言更加复杂,抽象程度高,但C++在一些图像处理、系统、控件的编程方面,实用性更强,具有自己的编程优势。在车载嵌入式系统的开发中,C和C++都具有重要的作用。C++语言所使用的面向对象的编程技术如封装、继承和多态性极大的提高了在大规模嵌入式编程应用中代码的可读性、可重用性和可移植性。C++在嵌入式系统中广泛使用的主要原因包括:

  1. C++为许多嵌入式系统所必需的高速、低级输入/输出操作提供了良好的支持;
  2. 应用程序复杂性的增加使得使用高级预语言比汇编语言更适合;
  3. C++编译器可以生成与C具有相似大小和RAM要求的代码;
  4. C++支持使用面向对象的设计方法;
  5. 在低成本硬件上移植性的要求的增长;
  6. 从建模包中自动生成C++代码的使用增加;
  7. 对开发系统和托管环境的兴趣日益增加,C++是一种可能的语言选择;

因此,在辅助驾驶、无人驾驶等软件系统的开发中,C++被广泛的应用。针对C++在汽车工业领域的安全规范也逐渐完善。AUTOSAR C++是ISO/IEC 14882标准在汽车工业子领域的应用规范。

一、AUTOSAR C++规范

AUTOSAR C++是MISRA C++:2008的更新,也是汽车工业领域的安全性编程规范,但AUTOSAR C++不仅限应用于汽车工业领域,也可以应用在其他的嵌入式系统。。在AUTOSAR C++之前,MISRA C++ 2008是C++在汽车等关键领域的编程指导规范。随着C++的标准不断的更新,MISRA C++ 2008已经不包括对C++11/14的规范要求。AUTOSAR C++ 的一些列规范在此背景下发布,弥补了MISRA C++:2008的不足。在MISRA C++:2008基础上,AUTOSAR C++:14的演变为:

  • C++语言的实质性演变/改进;
  • 在安全相关和关键环境中更广泛的使用面向对象的语言;
  • 可用的更好编译器;
  • 提供适合C++的更好的测试、验证和分析工具;
  • 提供更好的开发方法(如持续集成),以便更早的检测/处理错误;
  • 安全工程师对面向对象的编程语言的接受程度更高;
  • 开发团队对功能强大的C++语言特性的强烈需求;
  • 构建ISO 26262安全标准,包括HIC++, JSF++, CERTC++,C++核心指南;

除此之外,AUTOSAR在MISRA C++:2008基础之上指定了:

  • 哪些MISRA C规则已经过时,不需要遵守;
  • 一些更新的MISRA规则;
  • 一些额外的规则;

与MISRA-C一样,AUTOSAR C++的规范中的规则根据义务等级进行规则划分为:必需(强制性)、建议性。根据静态分析执行规则可以划分为:自动化、部分自动化、非自动化。绝大多数的规则都是能够通过静态分析自动强制执行。除此之外,这些规则还可以根据被分配的目标分类为:实施(代码、软件设计、架构)、验证(代码审查、分析、测试)、工具链(预处理器、编译器、链接器、编译器库)、基础设施(运行系统、硬件)。

二、AUTOSAR C++对安全性的理解

C++语言的不安全性主要来源有:开发者犯错、开发者对C++的误解、编译器不执行开发人员所期望的操作、编译器包含错误、运行是的错误。

  1. 开发者犯错:
    开发人员会犯很简单的错误,比如错误地输入了一个变量名,也可能会犯一些诸如误解了一个算法这样的复杂错误。编程语言与这种类型的错误有关。首先,语言的风格和表达性可以帮助或阻碍程序员清晰地思考算法。其次,该语言可以使输入错误将一个有效的构造变成另一个有效的(但无意的)构造变得容易或困难。第三,编译器在出现错误时可能检测到也可能不检测到错误,其主要原因如下:
    首先,在风格和表达性方面,C++可以用来编写布局良好、结构化和表达性强的代码。它还可以用来编写反常的和极其难以理解的代码。显然,后者在与安全相关的系统中是不可接受的。
    其次,C++的语法是相对容易犯输入错误,从而导致完全有效的代码。例如,键入“=”(赋值)而不是“==”(逻辑比较)太容易了,结果几乎总是有效的(但错误的),而在if语句的末尾加上一个额外的分号可以完全改变代码的逻辑。
    最后,C++的哲学是假设开发者知道他们正在做什么,这可能意味着如果出现错误,编译器允许开发者不注意这些错误而通过编译。在这方面,C++特别弱(尽管比C好)的一个领域是“类型检查”。C++不会反对程序员尝试在一个bool类型中存储一个浮点数。大多数这样的不匹配只是被迫变得兼容。如果 C++ 有一个方形钉和一个圆孔,它不会抱怨,而是让它们适合!
    示例:
    规则:M6-2-1: (强制性,实施,自动化)赋值运算符不得用于子表达式
x = y; 
x = y = z; // 不合规 
if ( x != 0 ) // 合规
{         foo ( ); 
}
bool b1 = x != y; // 合规
bool b2; 
b2 = x != y; // 合规
if ( ( x = y ) != 0 ) // 不合规 
{         foo ( ); 
}
if ( x = y ) // 不合规 
{         foo ( ); 
}
if ( int16_t i = foo ( ) ) // 合规 
{ 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. 开发者对C++的误解:
    开发人员可能会误解一种语言中构造的效果。有些语言比其他语言更容易产生这种误解。C语言++语言有许多领域容易出现开发人员引入的错误。例如,操作符优先级的规则定义良好,但很复杂,而且开发人员很容易在表达式中做出不正确的假设。
    示例:
    M5-0-2: 建议,实施,部分自动化)表达式中的 C++ 运算符优先规则应受到有限的依赖。
//何时强制性括号
赋值运算符的右侧操作数不强制性括号,除非右侧本身包含赋值表达式:
x = a + b; // 可以接受的
x = (a + b); // () 是不需要的

一元运算符的操作数不强制性括号:
x = a * -1; // 可以接受的
x = a * (-1); // () 不需要

否则,二元和三元运算符的操作数应为强制转换表达式(参见 ISO/IEC 14882:2003 [1] 的第 5.4(2) 节),除非表达式中的所有运算符都相同
x = a + b + c; // 可以接受, 但是考虑需求,对于a+b,不强制性括号
x = f ( a + b, c ); 
x = ( a == b ) ? a : ( a – b ); 
if ( a && b && c ) // 可以接受的
x = ( a + b )( c + d ); 
x = ( a * 3 ) + c + d; 
x = static_cast< uint16_t > ( a ) + b; // 不需要用来转换

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  1. 编译器不执行开发人员所期望的操作:
    如果一种语言的特性没有完全定义,或者不明确,那么开发人员可以对构造的含义做一个假设,而编译器对它的解释可能完全不同。C语言++语言中有许多领域没有被完全定义,因此不同编译器的行为可能因人而异。在某些情况下,行为甚至在单个编译器中也会有所不同,这取决于上下文。ISO/IEC14882:2003[1]包含了许多可能以这种方式有所不同的问题。然而,它并没有像C标准在其“可移植性问题”附件中那样列出它们。(MISRA C++:2008的附录B详细了列出了C++漏洞)
    示例:
    规则 M4-5-3: (强制性,实施,自动化)类型(普通)char 和 wchar_t 的表达式不得用作除赋值运算符 =、相等运算符 == 和 ! 之外的内置运算符的操作数 = 和一元 & 运算符。
    根本原因:对字符数据的处理可能会产生与开发人员期望相反的结果。 例如,ISO/IEC 14882:2003 [1] §2.2(3) 仅要求数字“0”到“9”具有连续的数值。
    例外:在特殊情况下,如果遵守相关限制,则可以使用以下运算符:
    二进制 + 运算符可用于将 0 到 9 范围内的整数值添加到“0”;
    二元-运算符可用于减去字符“0”;
    关系运算符 <、<=、>、>= 可用于确定字符(或宽字符)是否表示数字
char_t ch = ‘t’; // 合规
uint8_t v;
if ( ( ch >= ‘a’ ) && ( ch <= ‘z’ ) ) // 不合规
{ 
}
if ( ( ch >=0) && ( ch <=9) ) // 符合例外  
{ 
 v = ch – ‘0; // 符合例外 
 v = ch – ‘1; // 不合规
}
else 
{ 
 // ... 
} 
ch =0+ v; // 符合例外 
ch = ‘A’ + v; // 不合规

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. 编译器包含的错误:
    一个语言编译器(以及相关的链接器等)。它本身就是一个软件工具。编译器可能并不总是能正确地编译代码。例如,在某些情况下,它们可能不符合语言标准,或者它们可能只是包含“bug”。
    因为C语言++语言的某些方面很难理解,所以人们已经知道编译器编写者会误解该标准并错误地实现它。该语言的某些领域比其他领域更容易发生这种情况。此外,编译器的作者有时会有意识地选择改变标准。
    示例:
    规则 M1-0-2: (强制性,工具链,非自动化)仅当多个编译器具有通用的定义接口时,才应使用多个编译器。
    根本原因:多“编译器”(任何工具) 包括:
    混合语言、不同编译器、同一编译器的不同版本、同一编译器的不同配置。
    如果一个模块要用C++以外的语言实现,或者使用不同的编译器编译,需确保这个模块的正确集成。强制性考虑的问题包括:堆栈使用、参数传递、数据值的存储方式(长度、对齐、混叠、叠加等)。

  2. 运行时的错误:
    对于编译正确的代码,会出现一些不同的语言问题,但由于提供给它的特定数据,代码会在执行过程中导致错误。语言可以在可执行代码中构建运行时检查,以检测许多此类错误并采取适当的措施。 C++ 在提供运行时检查方面通常很差。这就是为什么由C++生成的代码往往很小且高效的原因之一,但在执行过程中检测错误时需要付出代价。C++编译器通常不为诸如算术异常(例如除以零)、溢出、指针地址的有效性或数组绑定错误等常见问题提供运行时检查。
    示例:
    规则 A18-0-2: (强制性,实施,自动化)应检查从字符串到数值的转换的错误状态。

std::uint16_t ReadFromStdin1() // 不合规
{ 
std::uint16_t a; 
std::cin >> a; // 没有检测到错误 
return a; 
}
std::uint16_t ReadFromStdin2() // 合规
{ 
std::uint16_t a; 
std::cin.clear(); // 清除所有标志位
std::cin >> a; 
if (std::cin.fail()) 
{ 
throw std::runtime_error{"unable to read an integer"}; 
} 
std::cin.clear(); // 为后续操作清除所有标志位
return a; 
} 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

三 总结

C++以其极高的编程效率在嵌入式系统开发中大放异彩,汽车嵌入式软件开发出高效安全的软件,需要严格的规范来对原生的C++标准进行约束。关注AUTOSAR C++的规则更新是智能汽车软件开发人员工程软件开发技能成熟的标志。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/831869
推荐阅读
相关标签
  

闽ICP备14008679号