代码实例辨析|autosarcpp14编程规范和MISRAC++:2008编程规范

原创 汽车电子与软件 2025-03-14 19:02

本文约14,200字,建议收藏阅读

         

作者 | 直观解

出品 | 汽车电子与软件



#01
AUTOSAR C++14 编程规范与 MISRA C++:2008 编程规范介绍


工业开发几乎不得不面对和遵从各种严格的编程规范,有些规则是从代码安全角度,有些则是从代码可读性易读性出发,有些是为以后方便扩展避免架构腐化而考虑的。


1、背景


AUTOSAR C++ 14 指南文件旨在作为 MISRA C++ 2008 的一次更新,并为 ISO/IEC 14882:2014 定义的现代C++提供编码指南。该编码标准主要应用于汽车行业,但同样也可用于需要嵌入式编程的其他行业,比如无人机编程和IoT编程。


  • MISRA C++:2008 是由汽车行业软件可靠性协会(MISRA)开发的一套针对C++语言的编码标准,旨在帮助开发者编写出更加安全和可靠的代码 。


  • AUTOSAR C++14 则是基于MISRA C++:2008 进行补充和完善的一套适用于汽车软件的编码规范,它增加了对现代C++(如C++11和C++14)的支持,并根据最新的编程实践改进了部分规则。

          

 

2、AUTOSAR C++14 编程规范


AUTOSAR C++14 编程规范的主要特点如下:


  • 扩展而非替代AUTOSAR C++14 并不是取代现有的MISRA C++标准,而是对其进行了扩展。它增加了新的C++编码规范、更新了现有规范,并删除了那些已经过时的规范(这是autosar组织的原文描述)。


  • 支持现代C++该规范特别强调对C++11和C++14的支持,以适应现代C++编程的最佳实践 。


  • 适用于安全关键系统AUTOSAR C++14 的目标是确保代码能够在安全关键系统中使用,特别是在汽车行业中 。


  • 标准化API和服务AP接口作为Adaptive AUTOSAR平台(也就是autosar AP,所谓自适应平台,用于高端计算)的一部分,AUTOSAR C++14 规范为API和服务接口提供了详细的定义 。

          

 

3、MISRA C++:2008 编程规范


MISRA C++:2008 编程规范的主要特点如下:   


  • 覆盖广泛MISRA C++:2008 包含了一系列规则和指导原则,涵盖了从编程实践到代码设计的多个方面,旨在帮助开发者避免潜在的编程错误。


  • 面向安全性该规范特别关注嵌入式系统中的安全性和可靠性问题,尤其是在汽车、航空和医疗设备等安全性至关重要的领域。


  • 规则详尽MISRA C++:2008 针对符合C++03标准的代码,共有228条编码规则,这些规则详细规定了如何编写安全可靠的代码。

          

 

4、对比与联系


  • 起点相同AUTOSAR C++14 是基于MISRA C++:2008 开发的,因此两者在许多规则上是一致的 。


  • 扩展与更新AUTOSAR C++14 在MISRA C++:2008 的基础上增加了对现代C++的支持,并根据最新的编程实践进行了调整和优化 。


  • 应用范围虽然两者都适用于汽车软件开发,但AUTOSAR C++14 更加专注于Adaptive AUTOSAR平台的需求,而MISRA C++:2008 则是一个更为通用的标准 。


从具体数据上讲,AUTOSAR C++14标准规定了342条规则,其中组成如下:


  • MISRA C++ 2008 中的 154 条规则未经修改直接采用 (67%);


  • 131条规则基于现有的C++标准;


  • 57条规则基于研究或其他文献或资源;


另外,AUTOSAR C++标准采用C++14而不采用C++17的主要原因之一是C++17标准中引入的新功能可能会给系统带来安全风险——检测和理解安全漏洞需要一些时间。此外,符合C++17标准的C++编译器仍然是新的,需要更多的测试和更好的支持才能用于安全关键型开发。因此,选择依靠C++14标准作为合理的中间选项。

   



#02
哪些规则MISRA C++ 2008有,而被AUTOSAR C++14取消

       

 

首先确实有些规则被AUTOSAR C++14规范取消(rejected)了,而且数量还不少。按笔者统计有如下18条:

          

 

  1. 2-10-3 (Required) A typedef name (including qualification, if any) shall be a unique identifier.  Rejected This rule is considered as too restrictive.


  2. 2-10-4 (Required) A class, union or enum name (including qualification, if any) shall be a unique identifier. Rejected This rule is considered as too restrictive.


  3. statement there shall be no more than one break or goto statement used for loop termination. Rejected The goto statement shall not be used, see: A6-6-1. There can be more than one break in an iteration statement.


  4. 6-6-5 (Required) A function shall have a single point of exit at the end of the function. Rejected Single point of exit approach does not necessarily improve readability, maintainability and testability. A function can have multiple points of exit.


  5. 9-6-2 (Required) Bit-fields shall be either bool type or an explicitly unsigned or signed integral type. Rejected - Permitted types changed. New rule introduced: A9-6-1. 


  6. 9-6-3 (Required) Bit-fields shall not have enum type. Rejected - Permitted types changed. New rule introduced: A9-6-1. 


  7. 9-6-4 (Required) Named bit-fields with signed integer type shall have a length of more than one bit. Rejected - Permitted types changed. New rule introduced: A9-6-1.


  8. 10-3-1 (Required) There shall be no more than one definition of each virtual function on each path through the inheritance hierarchy. Rejected - Rule already covered by A10-1-1


  9. 14-5-1 (Required) A nonmember generic function shall only be declared in a namespace that is not an associated namespace. Rejected - Usage of the ADL functionality is allowed. It is also used in STL for overloaded operators lookup in e.g. out streams, STL containers.


  10. 14-6-2 (Required) The function chosen by overload resolution shall resolve to a function declared previously in the translation unit. Rejected - Usage of the ADL functionality is allowed. It is also used in STL for overloaded operators lookup in e.g. out streams, STL containers. 


  11. 14-7-1 (Required) All class templates, function templates, class template member functions and class template static members shall be instantiated at least once. Rejected - It is allowed to not use all of the public methods of a class.


  12. 15-4-1 (Required) If a function is declared with an exceptionspecification, then all declarations of the same function (in other translation units) shall be declared with the same set of type-ids. Rejected - Dynamic exception specification was prohibited. Noexcept specifier shall be used instead.


  13. 16-0-3 (Required) #undef shall not be used. Rejected - The rule replaced with global rule: A16-0-1. 


  14. 16-0-4 (Required) Function-like macros shall not be defined. Rejected - The rule replaced with global rule: A16-0-1.


  15. 16-2-6 (Required) The #include directive shall be followed by either aor “filename” sequence. Rejected - These are the only forms allowed by the C++ Language Standard; No need for a new rule.


  16. 16-6-1 (Required) All uses of the #pragma directive shall be documented. Rejected - The #pragma directive shall not be used, see: A16-7-1


  17. 17-0-4 (Required) All library code shall conform to MISRA C++. Rejected - The rule replaced with A17-0-2 saying that all code shall conform to AUTOSAR C++14 Coding Guidelines.


  18. 18-4-1 (Required) Dynamic heap memory allocation shall not be used. Rejected - Dynamic heap memory allocation usage is allowed conditionally, see: A18-5-1, A18-5-2, A18-5-3          

 

以下我们以新旧对比的方式来分析这些规则,旧规则是指MISRA C++ 2008,新规则是指autosar c++ 14标准:

          

 

1、2-10-3 (Required) A typedef name (including qualification, if any) shall be a unique identifier.

          

 

**翻译:**  


typedef 名称(包括限定符,如果有)应是唯一的标识符。  


**拒绝原因:**  


该规则被认为过于严格。 

 

**代码示例:**

          

 

**反例:**

          

 

    namespace N {

        typedef int T;

    


    namespace M {

        typedef int T; // 违反规则,这是说旧规则里面即使不同命名空间typedef的name也必须唯一    

    }


在新规则,也就是autosar c++ 14标准里面,允许typedef name在合法的情况下重名。

            

 

2、2-10-4 (Required) A class, union or enum name (including qualification, if any) shall be a unique identifier.


类、联合或枚举名称(包括限定符,如果有)应是唯一的标识符。  


**拒绝原因:**  


该规则被认为过于严格。  


**代码示例:**

          

 

**反例:**

          

 

    namespace N {

        class T {};

    }

   

    namespace M {

        class T {}; // 违反旧规则,这是说旧规则里面即使不同命名空间class, union or enum name也必须唯一,新规则认为没必要   

 

    }

         

 

3、statement there shall be no more than one break or goto statement used for loop termination.


在循环终止时,break 或 goto 语句的使用不得超过一个。  


**拒绝原因:**  


goto 语句不应使用,参见 A6-6-1。在迭代语句中可以有多个 break。 

 

**代码示例:**    

          

 

**反例:**

          

 

    void example() {

        for (int i = 0; i < 10; ++i) {

            if (i == 5) {

                break; // 正确

            }

        }

    

        for (int j = 0; j < 10; ++j) {

            if (j == 5) {

                goto end; // 违反规则

            }

        }

    end:

        ;

    }


也就是说新规则彻底禁止goto语句,但允许多个break;语句来退出循环。  


4、6-6-5 (Required) A function shall have a single point of exit at the end of the function.


函数应在末尾有一个单一的出口点。


**拒绝原因:**  


单一出口点的方法不一定提高可读性、可维护性和测试性。函数可以有多处出口。  


**代码示例:**


**正例:**

          

 

    int find(int arr[], int size, int target) {

        for (int i = 0; i < size; ++i) {

            if (arr[i] == target) {    

                return i; // 多处出口点

            }

        }

        return -1; // 多处出口点

    }


这个新规则实际上和禁止goto语句紧密联系,如果一个函数确实有提早退出的需求,但又不能使用goto语句跳过后面的语句直达单一退出点,这会让程序员难于处理。所以只好放开允许多个退出点。    

          

 

5、9-6-2 (Required) Bit-fields shall be either bool type or an explicitly unsigned or signed integral type.


位字段应为 bool 类型或显式无符号或有符号整数类型。  


**拒绝原因:**  


允许的类型已更改。引入了新规则:A9-6-1。  


规则 A9-6-1(必选,实现,自动化)


位域应为无符号整数类型,或者是枚举类型(其底层类型为无符号整数类型)。


明确声明位域为无符号类型可以防止意外的符号扩展、溢出和实现定义的行为。具体来说,这条规则要求在定义位域时,必须使用无符号整数类型(如 unsigned int),或者使用枚举类型,并且该枚举类型的底层类型是无符号整数类型。这样做是为了避免由于符号扩展引起的潜在问题,例如当位域值被赋给有符号整数时可能出现的符号扩展问题。此外,这也可以防止因编译器实现差异导致的未定义行为或实现定义的行为。

          

 

**代码示例:**


1 // $Id: A9-6-1.cpp 271715 2017-03-23 10:13:51Z piotr.tanski $2 #include3 enum class E1 : std::uint8_t4 {5 E11,6 E12,7 E13   8 };9 enum class E2 : std::int16_t10 {11 E21,12 E22,13 E2314 };15 enum class E316 {17 E31,18 E32,19 E3320 };21 enum E422 {23 E41,24 E42,25 E4326 };27 class C28 {29 public:30 std::int32_t a : 2// Non-compliant - signed integral type,位域,指定了bit长度31 std::uint8_t b : 2U// Compliant,位域,指定了bit长度32 bool c : 1// Non-compliant - it is implementation-defined whether bool is33 // signed or unsigned34 char d : 2// Non-compliant35 wchar_t e : 2// Non-compliant36 E1 f1 : 2// Compliant37 E2 f2 : 2// Non-compliant - E2 enum class underlying type is signed38 // int 39 E3 f3 : 2// Non-compliant - E3 enum class does not explicitly define40 // underlying type41 E4 f4 : 2// Non-compliant - E4 enum does not explicitly define underlying42 // type43 };44 void fn() noexcept45 {46 C c;47 c.f1 = E1::E11;48 } 

位域(bit field)是C语言中的一种特殊数据结构,它允许在结构体或联合体中以位为单位来指定成员的长度。位域主要用于节省存储空间,并简化对某些硬件寄存器的操作。根据C语言标准,位域应为无符号整数类型(unsigned int),或者是枚举类型(其底层类型为无符号整数类型)。这是因为无符号整数类型可以确保所有分配给该字段的位都是有效的,而不会出现符号扩展等问题。


位域的定义格式如下:


struct {

    unsigned int member_name : number_of_bits;

} struct_name;


其中:


  • member_name 是位域成员的名称。

  • number_of_bits 是该成员占用的位数。


简而言之,autosar C++14的新规则简化了MISRA的旧规则,位域只允许无符号整数或者底层为无符号整数的枚举类型。而MISRA的旧规则还允许位域使用bool类型。

          

 

6、9-6-3 (Required) Bit-fields shall not have enum type.


位字段不应具有枚举类型。  


**拒绝原因:**  


允许的类型已更改。引入了新规则:A9-6-1(参见前一条)。  


**代码示例:**

          

 

    

**反例:**

          

 

    enum E { A, B };

    struct S {

        E x : 1; // 违反规则

    };

            

 

**正例:**

          

 

    struct S {

        bool x : 1; // 符合规则

        unsigned int y : 1; // 符合规则

    };

              

 

7、9-6-4 (Required) Named bit-fields with signed integer type shall have a length of more than one bit.


带有有符号整数类型的命名位字段应长度超过一个位。  


**拒绝原因:**  


允许的类型已更改。引入了新规则:A9-6-1。  


MISRA的这条旧规则彻底取消了,新规则不允许位域bit-fields使用有符号整数。 

          

 

8、10-3-1 (Required) There shall be no more than one definition of each virtual function on each path through the inheritance hierarchy.


在继承层次结构中的每条路径上,每个虚函数最多只能有一个定义。  


**拒绝原因:**  


该规则已被 A10-1-1 覆盖。 


规则 A10-1-1(必选,实现,自动化)


类不应从超过一个基类派生,除非这个基类是接口类。


多重继承会使派生类暴露于多个实现中。这使得代码更难以维护和跟踪。具体来说,这条规则建议一个类不应该从多于一个非接口类派生。多重继承可能会引入复杂性和潜在的冲突,因为派生类需要同时处理多个基类的实现细节。这不仅增加了理解和维护代码的难度,还可能导致一些难以调试的问题。通过限制类只从一个非接口类派生,可以简化类的设计和实现,使代码更加清晰和易于维护。    

 

**代码示例:**


1 // $Id: A10-1-1.cpp 271715 2017-03-23 10:13:51Z piotr.tanski $2 #include3 class A4 {5 public:6 void f1() noexcept(false) {}78 private:9 std::int32_t x{0};10 std::int32_t y{0};11 };12 class B13 {14 public:15 void f2() noexcept(false) {}1617 private:18 std::int32_t x{0};19 };20 class C : public A,21 public B // Non-compliant - A and B are both not interface classes22 {23 };

简而言之,新旧规则都不允许多重继承(除了多重继承接口类之外),新规则比旧规则更严格。    

          

 

9、14-5-1 (Required) A nonmember generic function shall only be declared in a namespace that is not an associated namespace.


非成员泛型函数只能在不是关联命名空间的命名空间中声明。 

 

**拒绝原因:**  


新规则允许使用 ADL 功能。它也用于 STL 中的操作符重载查找,例如输出流、STL 容器。  


C++中的ADL(Argument-Dependent Lookup,参数依赖查找)是一种编译器查找未限定函数名称的机制。它允许编译器根据传递给函数的参数类型来决定在哪些命名空间中查找函数定义。这意味着编译器不仅会在当前作用域内查找函数,还会扩展到包含这些参数类型的命名空间中进行查找。


这个功能非常像Java中根据函数参数列表来动态加载满足函数参数列表的备选函数。


ADL的一个常见应用场景是STL库中的 swap 函数。std::swap 是一个广为人知的例子,它利用了ADL机制,使得用户可以在不使用 std:: 前缀的情况下调用 swap 函数。此外,ADL还广泛用于标准库中的其他算法函数,如 std::begin 和 std::end,它们可以操作自定义容器类型而无需显式指定命名空间。


**代码示例:**

          

 

**反例:**

          

 

    namespace N {

        template

        void print(T t) {

            std::cout << t << std::endl;

        }

    }

             

 

**正例:**

          

 

    namespace N {

        template T>          

        void print(T t) {

            std::cout << t << std::endl;

        }

    }

    

    void example() {

        N::print(5); // 使用 ADL

    }

           

 

10、14-6-2 (Required) The function chosen by overload resolution shall resolve to a function declared previously in the translation unit.


通过重载解析选择的函数应解析为在翻译单元中先前声明的函数。  


这里是说原来在MISRA中的规则14-6-2 在autosar c++14中被取消了


**拒绝原因:**  


新规则允许使用 ADL 功能。它也用于 STL 中的操作符重载查找,例如输出流、STL 容器。  

             

 

11、14-7-1 (Required) All class templates, function templates, class template member functions and class template static members shall be instantiated at least once.


所有类模板、函数模板、类模板成员函数和类模板静态成员至少实例化一次。  


**拒绝原因:**  


旧规则太严格了,新规则允许不实例化类的所有公共方法。  


**代码示例:**

          

 

**反例:**

          

 

    template

    class MyClass {

    public:    

        void method() {}

    };

    

    void example() {

        MyClass obj;

        // obj.method(); // 不实例化obj.method();,在autosar的新规则中是允许的

    }

           

 

12、15-4-1 (Required) If a function is declared with an exceptionspecification, then all declarations of the same function (in other translation units) shall be declared with the same set of type-ids.


如果一个函数被声明了一个异常规范,则在同一函数的不同翻译单元中的所有声明都应具有相同的一组类型 id。  


**拒绝原因:**  


动态异常规范被禁止。应使用 noexcept 规范。  


实践表明,动态异常规范即使对程序员老手也会很难掌握,由于它复杂的规则。


**代码示例:**

          

 

**反例:**

          

 

    void f() throw(); // 违反规则

    

    void g() throw(int) {

        // 函数定义,表示只会抛出int型异常

    }

              

 

**正例:**    

          

 

    void f() noexcept;

    

    void g() noexcept {

        // 函数定义,表示不会抛出任何异常

    }


实际上,动态异常规范(dynamic exception specifications)在 C++11 中被弃用,并在 C++17 中完全移除,主要原因在于它们在实践中存在诸多问题。相比之下,noexcept 规范提供了一种更简洁、更可靠的方式来声明函数不会抛出异常。


动态异常规范的问题


  • 弱强制性:动态异常规范并不是严格强制的。如果一个函数声明了它只会抛出某些类型的异常,但实际上抛出了其他类型的异常,编译器并不会立即报错,而是会在运行时调用 std::unexpected 函数。


  • 性能开销:动态异常规范会引入额外的运行时开销,因为编译器需要检查和处理这些规范。这使得代码执行效率受到影响。


  • 不直观的行为:动态异常规范的行为并不总是符合开发者的预期。例如,它们不是函数类型的一部分,因此不能用于重载解析或模板推导。


noexcept 规范的优势


  • 简单明了:noexcept 关键字明确地告诉编译器和开发者,该函数不会抛出任何异常。这种声明更加直观,易于理解和维护。


  • 优化机会:当编译器知道某个函数不会抛出异常时,它可以进行更多的优化。例如,编译器可以省略与异常处理相关的代码,从而提高程序的性能。


  • 更好的错误处理:使用 noexcept 可以帮助确保在异常传播过程中不会发生意外终止。如果一个标记为 noexcept 的函数确实抛出了异常,程序将立即调用 std::terminate 终止执行,避免了未定义行为。


使用建议


  • 尽量使用 noexcept:对于那些确实不会抛出异常的函数,尤其是移动构造函数和移动赋值运算符,应该使用 noexcept 标记。这不仅提高了代码的可读性和安全性,还能带来潜在的性能提升。


  • 避免使用动态异常规范:由于动态异常规范已被弃用且存在诸多问题,应尽量避免在新代码中使用它们。对于旧代码,建议逐步迁移到 noexcept 或其他更现代的异常处理机制。通过采用 noexcept 规范,开发者可以获得更清晰、更高效的代码,同时避免动态异常规范带来的复杂性和潜在问题。    

          

         

 

13、16-0-3 (Required) #undef shall not be used.


不得使用 #undef。  


**拒绝原因:**  


该规则已被全局规则 A16-0-1 替换。

  

A16-0-1 原文和解释如下:


Rule A16-0-1 (required, implementation, automated) The pre-processor shall only be used for unconditional and conditional file inclusion and include guards, and using the following directives: (1) #ifndef, (2) #ifdef, (3) #if, (4) #if defined, (5) #elif, (6) #else, (7) #define, (8) #endif, (9) #include. 


Rationale C++ provides safer, more readable and easier to maintain ways of achieving what is often done using the pre-processor. The pre-processor does not obey the linkage, lookup and function call semantics. 


规则A16-0-1(必需,实现,自动化)预处理器应仅用于无条件和条件文件包含以及包含保护,并使用以下指令:(1)#ifndef,(2)#ifdef,(3)#if,(4)#if defined,(5)#elif,(6)#else,(7)#define,(8)#endif,(9)#include。  注意,这里允许使用(1)-(9)不包含#undef。


理由是C++提供了更安全、更易读和更易于维护的方法来实现通常使用预处理器所做的事情。预处理器不遵循链接、查找和函数调用语义。

          

           

 

**代码示例:**

          

 

**反例:**

          

 

    #define MACRO 1

    #undef MACRO // 违反规则

            

 

**正例:**

          

 

    #define MACRO 1    

    // 不使用 #undef


就是说新旧规则都不允许用#undef,工业编程一般不允许#undef主要是安全性和可读性考虑:

 

 

理由

描述

避免混淆和错误

宏定义取消后可能导致标识符重复定义,增加错误风险

提高代码可读性和维护性

减少复杂度,使代码更易理解和维护

遵循工业标准

符合《MISRA-C-2004》等标准的要求

防止宏定义冲突

通过设计避免宏定义冲突,而不是依赖 #undef

          

          

 

14、16-0-4 (Required) Function-like macros shall not be defined.


不得定义函数样式的宏。  


**拒绝原因:**  


该规则已被全局规则 A16-0-1 替换。

  

**代码示例:**

          

 

**反例:**

          

 

    #define add(x, y) ((x) + (y)) // 违反规则

           

 

**正例:**

          

 

    

    int add(int x, int y) {

        return x + y; // 符合规则

    }


全局规则 A16-0-1 已经确定预定义语句,比如宏,只能用于有条件或者无条件的文件包含或者包含检查,而不能用于计算。简单说新规则就是不要用宏来干函数的事情。

          

 

15、16-2-6 (Required) The #include directive shall be followed by either a or “filename” sequence.


#include 指令后面应跟一个或 "filename" 序列。  


**拒绝原因:**  


这是 C++ 语言标准所允许的唯一形式;不需要新的规则。  


**代码示例:**


    #include   // 符合规则

              

 

16、16-6-1 (Required) All uses of the #pragma directive shall be documented.


#pragma 指令的所有使用都应记录。

  

**拒绝原因:**  


新规则决定#pragma 指令不应使用,参见 A16-7-1。  

   

pragma 指令是一种预处理指令用于控制编译器,为程序员提供了一种机制,可以在保持与C和C++语言完全兼容的情况下,向编译器传递特定于主机或操作系统的特征信息。这些指令通常用于控制编译器的行为,例如优化、警告处理、代码布局等,而不改变程序的逻辑 。


禁止 #pragma 指令的原因包括:


1. 编译器依赖性#pragma 指令是编译器特定的,不同编译器对同一 #pragma 指令的解释可能不同,甚至有些编译器根本不支持某些 #pragma 指令。这会导致代码的可移植性问题。


2. 维护复杂性#pragma 指令的使用可能会增加代码的复杂性和维护难度。对于大型项目,过多的 #pragma 指令可能会使代码难以理解和调试,尤其是在团队开发环境中 。


3. 潜在的副作用某些 #pragma 指令可能会导致意外的行为或副作用。例如,禁用编译器警告可能会隐藏潜在的问题,影响代码质量。因此,在严格的质量控制环境下,可能会限制或禁止使用 #pragma 指令。 

  

4. 标准化问题由于 #pragma 指令不是C或C++标准的一部分,过度依赖它们可能会导致代码不符合标准规范,从而在未来的版本中出现问题 。

          

 

17、17-0-4 (Required) All library code shall conform to MISRA C++.


所有库代码应符合 MISRA C++。 

 

**拒绝原因:**  


该规则已被 A17-0-2 取代,要求所有代码符合 AUTOSAR C++14 编码准则。  


 这一条是自然必有的。

          

 

18、18-4-1 (Required) Dynamic heap memory allocation shall not be used.


不得使用动态堆内存分配。  


**拒绝原因:**  


新规则中动态堆内存分配的使用是条件允许的,参见 A18-5-1, A18-5-2, A18-5-3。


**代码示例:**


**反例:**

          

 

    int* p = new int; // 违反旧规则

    delete p;

             

 

**正例:**


    // 条件允许的动态堆内存分配

    void safe_alloc() {

        int* p = nullptr;

        try {

            p = new int(10);

        } catch (const std::bad_alloc&) {

            // 处理异常    

        }

        delete p;

    }


这条在旧规则中是为了防止segment error,段错误。这种错误极难排查,是代码运行时分配的堆内存覆盖了其他已经分配的内存。但是C++ 14的智能指针已经防止了这种错误。

          

 

 


#03
MISRA 2008和autosar c++14的常见共同点

 

如前所述,MISRA 2008和autosar c++14 90%以上的规则都是共用的,或者只有微小差异。如果要像前面分析它们的差异一样分析它们的共同点,那么等价于几乎是分析所有MISRA 2008和autosar c++14规则,这是单篇文章做不到的。


限于篇幅,本文选取一些有代表性的共同点分析如下:


0-1-1 (Required) A project shall notcontain unreachable code.


0-1-2 (Required) A project shall not contain infeasible paths.


0-1-3 (Required) A project shall not contain unused variables.


0-1-4 (Required) A project shall not contain non-volatile POD variables having only one use.


0-1-5 (Required) A project shall not contain unused type declarations.


0-1-8 (Required) All functions with void return type shall have external side effect(s).


0-1-9 (Required) There shall be no dead code.


0-1-11 (Required) There shall be no unused parameters (named or unnamed) in non-virtual functions. 1 - Identical M0-1-11 -


0-1-12 (Required) There shall be no unused parameters (named or unnamed) in the set of parameters for a virtual function and all the functions that override it. 1 - Identical M0-1-12 -


0-2-1 (Required) An object shall not be assigned to an overlapping object.

1 - Identical M0-2-1 -


0-3-1 (Document) Minimization of run-time failures shall be ensured by the

use of at least one of: (a) static analysis tools/techniques; (b) dynamic analysis tools/techniques; (c) explicit coding of checks to handle run-time faults.


0-3-2 (Required) If a function generates error information, then that error information shall be tested.


0-4-1 (Document) Use of scaled-integer or fixed-point arithmetic shall be documented.


0-4-2 (Document) Use of floating-point arithmetic shall be documented.


1-0-2 (Document) Multiple compilers shall only be used if they have a

common, defined interface.


1-0-3 (Document) The implementation of integer division in the chosen

compiler shall be determined and documented.


3-2-2 (Required) The One Definition Rule shall not be violated.


3-2-3 (Required) A type, object or function that is used in multiple translation units shall be declared in one and only one file.


3-2-4 (Required) An identifier with external linkage shall have exactly one

definition.


3-3-1 (Required) Objects or functions with external linkage shall be declared

in a header file.


3-3-2 (Required) If a function has internal linkage then all re-declarations shall include the static storage class specifier.


3-4-1 (Required) An identifier declared to be an object or type shall be defined in a block that minimizes its visibility.


3-9-1 (Required) The types used for an object, a function return type, or a

function parameter shall be token-for-token identical in all declarations and

re-declarations.


9-5-1 (Required) Unions shall not be used.

          

 

0-1-1 (必选) 项目中不得包含不可达代码。


解释:


不可达代码是指在程序执行过程中永远不会被执行到的代码。这些代码会增加程序的复杂性,也可能会导致混淆和错误。示例:


void foo() {

    int x = 10;

    if (false) { // 始终为假

        x = 20;

    }

    std::cout << x << std::endl; // 输出10

}


在这个例子中,x = 20; 是不可达代码,因为它永远不会被执行。    

          

 

0-1-2 (必选) 项目中不得包含不可行路径。


解释:


不可行路径是指在正常运行条件下不可能被执行的代码路径。这通常是因为某些条件始终不满足导致的。示例:


void bar(int y) {

    if (y > 100 && y < 0) { // 不可能同时大于100且小于0

       ...

    } else {

        ...

    }

}

          

 

0-1-3 (必选) 项目中不得包含未使用的变量。


解释:


未使用的变量是指声明了但从未在程序中使用的变量。这会增加代码的复杂性并可能导致误解。示例:


void baz() {

    int unusedVariable = 10; // 未使用

}

          

 

0-1-4 (必选) 项目中不得包含仅使用一次的非易变POD变量。


解释:


POD(Plain Old Data)类型是指简单的数据结构,如整数、浮点数等。如果一个非易变的POD变量只使用一次,那么它可能是冗余的。示例:


void qux() {

    const int temp = 10; // 仅使用一次

   ...

}

          

 

0-1-5 (必选) 项目中不得包含未使用的类型声明。

    

解释:


未使用的类型声明typedef是指声明了但从未在程序中使用的类型。这会增加代码的复杂性并可能导致误解。

          

 

0-1-8 (必选) 所有返回类型为void的函数必须具有外部副作用。


解释:


如果一个函数的返回类型是void,那么它应该至少有一个外部副作用,例如修改全局状态或调用其他函数。


示例:


void modifyGlobalState() {

    globalVar = 10; // 修改全局变量

}

          

 

0-1-9 (必选) 项目中不得包含死代码。


解释:


死代码是指在程序执行过程中永远不会被执行的代码。这会增加代码的复杂性并可能导致误解。示例:


void deadCodeExample() {

    int x = 10;

    if (false) {

        x = 20;

    }

    ...

}

          

 

0-1-11 (必选) 在非虚函数中不得包含未使用的参数(命名或未命名)。


解释:


未使用的参数是指在函数中声明了但从未使用的参数。这会增加代码的复杂性并可能导致误解。示例:


void unusedParameterExample(int unusedParam) { // 未使用参数

    ...

}    

          

 

0-1-12 (必选) 在虚函数及其所有重载函数中不得包含未使用的参数(命名或未命名)。


解释:


未使用的参数是指在虚函数及其所有重载函数中声明了但从未使用的参数。这会增加代码的复杂性并可能导致误解。示例:


class Base {

public:

    virtual void virtualFunction(int unusedParam) { // 未使用参数

       ...

    }

};

          

 

class Derived : public Base {

public:

    void virtualFunction(int unusedParam) override { // 未使用参数

        ...

    }

};

          

 

0-2-1 (必选) 对象不能赋值给重叠的对象。


解释:


如果两个对象在内存中重叠,直接赋值可能会导致意外的行为。示例:


struct OverlappingStruct {

    int a;

    int b;

};

          

 

void overlapAssignment(OverlappingStruct& s1, OverlappingStruct& s2) {

    s1 = s2; // 直接赋值可能导致重叠问题

}

          

 

    

0-3-1 (文档) 应使用至少一种方法来确保运行时故障最小化:


(a) 静态分析工具/技术;


(b) 动态分析工具/技术;


(c) 显式编码检查以处理运行时故障。

          

 

0-3-2 (必选) 如果函数生成错误信息,则该错误信息必须进行测试。


解释:


如果函数生成错误信息,那么这些错误信息必须经过测试以确保其正确性。示例:


void generateErrorInfo() {

    int errorCode = getErrorCode(); // 假设getErrorCode()返回错误码

    if (errorCode != 0) {

        throw std::runtime_error("Error occurred.");

    }

}


每一种抛出的错误信息都必须有对应的测试用例覆盖住。

          

 

0-4-1 (文档) 使用缩放整数或定点算术应进行文档记录。


解释:


如果使用缩放整数或定点算术,应进行文档记录以便其他开发者理解。示例:


// 文档记录缩放整数或定点算术

int scaleInteger(int value, int scale) {

    return value * scale;

}

          

 

0-4-2 (文档) 使用浮点算术应进行文档记录。


解释:


如果使用浮点算术,应进行文档记录以便其他开发者理解。示例:


// 文档记录浮点算术

double performFloatingPointOperation(double a, double b) {

    return a + b;

}

          

 

1-0-2 (文档) 只有在它们具有共同定义的接口的情况下才应使用多种编译器。 

   

解释:


只有在多种编译器之间存在共同定义的接口时,才能使用多种编译器。并且要在文档中记录使用了哪些编译器。

          

 

1-0-3 (文档) 应确定并记录所选编译器中的整数除法实现。


解释:


应确定并记录所选编译器中的整数除法实现。不同编译器的整数除法实现是不同的,有不同的行为,必须在文档中记录所用编译器特定的整数除法行为。

          

 

3-2-2 (必选) 不得违反单定义规则。


解释:


单定义规则意味着在一个项目中,对于一个给定的标识符,只能有一个定义。示例:


// 违反单定义规则

int x = 10;

int x = 20; // 重复定义

          

 

3-2-3 (必选) 在多个翻译单元中使用的类型、对象或函数应在其中一个文件中声明。


解释:


如果一个类型、对象或函数在多个翻译单元中使用,应在其中一个且仅一个文件中声明。


在多个翻译单元中使用的类型、对象或函数应在其中一个文件(不要更多,不要重复)中声明,以确保编译器能够正确识别和处理这些实体。具体来说,翻译单元是指一个源文件(如 .cpp 文件)及其直接或间接包含的所有头文件(如 .h 或 .hpp 文件)。每个翻译单元可以独立编译成目标文件(如 .obj 或 .o 文件),最终通过链接器组合成可执行文件或库。


为了确保不同翻译单元之间的一致性和避免重复定义问题,应该遵循以下几点:


a、单一声明原则:


类型、对象或函数的声明应当只在一个地方进行,通常是头文件中。这可以通过在头文件中使用 #include 指令来实现,使得所有需要该声明的源文件都能访问到它。


b、内联函数和静态函数的特殊处理:


内联函数可以在多个翻译单元中定义,但必须确保每个定义都是相同的。这是因为内联函数的定义会在每个使用它的翻译单元中展开。


静态函数的作用域仅限于当前翻译单元,因此可以在多个翻译单元中定义相同名称的静态函数而不发生冲突。    


c、外部链接和内部链接:


具有外部链接的对象或函数可以在多个翻译单元中引用,但只能在一个翻译单元中定义。


具有内部链接的对象或函数(如静态变量或函数)仅限于当前翻译单元,不会与其它翻译单元中的同名实体冲突。


d、头文件保护:


为了避免头文件被多次包含,通常会使用预处理器指令(如 #ifndef、#define 和 #endif)来防止重复定义。


示例:


假设有一个类 MyClass 和一个全局函数 myFunction,它们将在多个翻译单元中使用。可以按照以下方式组织代码:


头文件 

myclass.h

#ifndef MYCLASS_H

#define MYCLASS_H

          

 

class MyClass {

public:

    void doSomething();

};

          

 

void myFunction();

          

 

#endif // MYCLASS_H

源文件 myclass.cpp

#include "myclass.h"

          

 

void MyClass::doSomething() {

    // 实现细节

}

          

 

void myFunction() {    

    // 实现细节

}


使用 MyClass 和 myFunction 的其他源文件


#include "myclass.h"

          

 

int main() {

    MyClass obj;

    obj.doSomething();

    myFunction();

    return 0;

}


通过这种方式,MyClass 和 myFunction 的声明只在头文件 myclass.h 中出现一次,而定义则在 myclass.cpp 中提供。这确保了不同翻译单元之间的声明一致性,并避免了重复定义的问题。

          

 

3-2-4 (必选) 具有外部链接的标识符应仅仅有一个定义。


解释:


具有外部链接的标识符应仅仅有一个定义,否则会导致链接错误。示例:

外部链接就是跨模块调用,也就是公有变量或者函数,可以被其定义所在模块之外的模块调用。这样就容易导致定义重复。

          

 

3-3-1 (必选) 具有外部链接的对象或函数应在头文件中声明。


解释:


具有外部链接的对象或函数应在头文件中声明,以便其他文件可以引用它们。示例:


// 在header.h中

#ifndef HEADER_H

#define HEADER_H

          

 

extern int globalVar; //外部变量

          

 

#endif // HEADER_H    

          

 

3-3-2 (必选) 如果函数具有内部链接,则所有重新声明应包括静态存储类说明符。


解释:


在C和C++编程语言中,如果一个函数具有内部链接(internal linkage),那么所有对该函数的重新声明都应当包含静态存储类说明符(static)。这是因为static关键字不仅影响变量的存储持续时间,还决定了函数或变量的链接属性。具体来说,使用static修饰的函数仅在定义它的源文件内可见,即它具有内部链接。


静态存储类说明符的作用


  • 内部链接:当函数或全局变量被声明为static时,它们只在当前源文件中可见。这意味着其他源文件无法直接访问这些函数或变量。这种特性有助于减少命名冲突,并提高代码的模块化程度。


  • 生命周期:对于局部变量,static关键字使得该变量在整个程序运行期间保持存在,而不是每次进入和离开作用域时创建和销毁。这可以用于保存状态信息,例如计数器或缓存结果。


  • 内存分配:静态变量在程序启动时分配内存,并在程序结束时释放。因此,静态变量在整个程序执行期间始终存在。

          

 

如果函数具有内部链接,所有重新声明应包括静态存储类说明符static。示例:


// 在source.cpp中

static void internalFunction() {

    // 函数体

}

          

 

3-4-1 (必选) 声明为对象或类型的标识符应在最小可见性的块中定义。


解释:


声明为对象或类型的标识符应在最小可见性的块中定义,以减少代码的耦合度。

          

 

3-9-1 (必选) 对象、函数返回类型或函数参数使用的类型在所有声明和重新声明中应逐个字符相同。


解释:


对象、函数返回类型或函数参数使用的类型在所有声明和重新声明中应逐个字符相同,以避免类型不匹配的问题。示例:    


// 在header.h中

#ifndef HEADER_H

#define HEADER_H

          

 

typedef int MyInt;

          

 

#endif // HEADER_H

          

 

// 在source.cpp中

#include "header.h"

          

 

void function(MyInt param) {

    // 函数体

}

          

 

9-5-1 (必选) 不得使用联合体union。


解释:


联合union是一种特殊的数据结构,同一时刻只能存储其中的一个成员。这可能导致难以追踪的数据问题,因此应避免使用。示例:


// 不推荐使用联合

union UnionExample {

    int a;

    float b;

};


          

 

          

 

#04
些值得注意的显著规则

 

笔者个人认为,最值得注意的规则是要求不要用递归,也就是规则7-5-4 :


7-5-4 (Advisory) Functions should not call themselves, either directly or indirectly. 2 - Small differences A7-5-1 Obligation level changed to “Required”. Example reworked.    


MISRA里面这个规则是建议,到autosar C++14则是“Required”.


背后原因很好理解,递归程序在依靠栈展开的,递归层次较深时很容易栈溢出StackOverflow造成程序崩溃,在车载程序中禁止是可以理解的。


其次是autosar C++ 14不准用/**/这种C风格的注释,而MISRA是可以用的。


2-7-1 (Required)

The character sequence /* shall not be used within a C-style comment.

3 - Significant differences A2-8-4 Using the C-style comments is not allowed.

          

 

再就是自加自减运算符建议不要和其它运算符混用,这个目的是为了可读性。


5-2-10 (Advisory) The increment (++) and decrement (–) operators shall not

be mixed with other operators in an expression.

          

 

5-18-1 (Required) The comma operator shall not be used.


要求不允许使用逗号运算符


for 循环中的多变量初始化就是一个典型:逗号运算符常用于 for 循环中,以便在同一行代码中初始化多个变量。例如:


for (int i = 0, j = 10; i < j; ++i, --j) {

    // 循环体

}


整个逗号表达式的值是最后一个表达式的值。例如,在表达式 a = 1, b = 2 中,表达式的值为 b = 2 的结果,即 2。


如果用在条件判断中,会引起难于阅读的问题。比如:


if ((a = getValue()), a > 0) {

    // 条件成立时的代码

}


意思是先获取a的值,再返回a > 0的真伪值,但这样写违反good Style。

          

 

再一个对C语言程序员不太适应的规则是不让用


27-0-1 (Required) The stream input/output library shall not be used.


对C程序员耳熟能详的语句如下:


1. 使用 printf 和 scanf 进行基本输入输出    


#include

          

 

int main() {

    int num;

    printf("请输入一个整数: "); // 输出提示信息

    scanf("%d", &num);         // 读取用户输入的整数

    printf("你输入的整数是: %d\\n", num); // 输出用户输入的整数

    return 0;

}

          

 

2. 使用 fopen、fprintf、fscanf 和 fclose 处理文件


#include

          

 

int main() {

    FILE *file = fopen("example.txt", "w"); // 打开文件进行写入

    if (file == NULL) {

        printf("无法打开文件\\n");

        return 1;

    }

          

 

    fprintf(file, "Hello, World!\\n"); // 向文件中写入字符串

    fclose(file); // 关闭文件

          

 

    file = fopen("example.txt", "r"); // 打开文件进行读取

    if (file == NULL) {

        printf("无法打开文件\\n");

        return 1;

    }

          

 

    char buffer[100];

    fscanf(file, "%s", buffer); // 从文件中读取字符串    

    printf("文件内容: %s\\n", buffer);

    fclose(file); // 关闭文件

          

 

    return 0;

}

          

 

3. 使用 fgets 安全地读取字符串


fgets 函数可以安全地读取一行文本,并且可以指定最大读取长度,避免缓冲区溢出问题。


#include

          

 

int main() {

    char line[100];

    printf("请输入一行文本: ");

    fgets(line, sizeof(line), stdin); // 从标准输入读取一行文本

    printf("你输入的文本是: %s", line); // 输出用户输入的文本

    return 0;

}

          

 

4. 使用 perror 处理错误


perror 函数用于输出系统错误信息,通常在文件操作失败时使用。


#include

          

 

int main() {

    FILE *file = fopen("nonexistent.txt", "r");

    if (file == NULL) {

        perror("fopen"); // 输出错误信息

        return 1;

    }

    fclose(file);

    return 0;    

}


而不推荐使用 的原因除了效率还有安全:


  • 全局命名空间污染:


使用<cstdio>会将所有符号引入全局命名空间,这可能会导致命名冲突和其他问题。相比之下, 中的符号被封装在 std 命名空间中,避免了这种污染 。


  • 性能问题:


C++的 库通常比C风格的 更高效,尤其是在处理大量数据时。


库可以与C++的异常处理机制集成,允许使用 try 和 catch 块来处理输入输出中的异常。而 中的函数通常不提供内置的异常处理机制 。


  • 线程安全:


库提供了更好的线程安全支持,而 在这方面表现较差,特别是在多线程环境中 。


    • 标准化和未来兼容性:


    C++标准更倾向于使用 ,并且一些现代编译器可能不再完全支持 或者对其支持有限 。


    使用其实在编程工作量上比 明显要大,同样一句话要多打30%的字母,但是在现代IDE自动联想功能下,这个缺陷并不大,而安全性和代码性能的考量更重要,所以才有禁用的要求。



     

    #05
    总  结

        

    autosar c++14标准,及其前身MISRA 2008,规则定义十分详细和丰富,几乎涵盖了C和C++语言全部的语言特性,而且很多规则都是从一线实战编程中反复总结得出。


    由于C和C++语言的语言特性本身是高度贴合计算机底层原理的,C语言素有“最接近机器语言的高级语言”之称。这更加增添了对这些工业级编程标准的理解难度。


    因此其学习和理解不仅具有一定难度,部分规则甚至在新手程序员看来是“反直觉的” ,“反习惯的”。


    但是AUTOSAR C++14工业编程规范的必要性在于它为汽车行业的软件开发提供了全面且严格的标准,确保了代码的安全性、可靠性和可维护性,同时适应了现代C++语言的发展。此外,它还促进了跨厂商的标准化开发,支持复杂功能的实现,特别是autosar Adaptive平台上OTA,流媒体处理、智能座舱、自动驾驶等等高级复杂功能的实现。


    学习和遵循这些标准中会领会到大量编程实践的实战经验和c++新语言特性,成为现代汽车电子开发的标配标准。    

              

     



    / END /


           

     

        





    汽车电子与软件 主要介绍汽车电子软件设计相关内容,每天分享一篇技术文章!
    评论 (0)
    • 前言在快速迭代的科技浪潮中,汽车电子技术的飞速发展不仅重塑了行业的面貌,也对测试工具提出了更高的挑战与要求。作为汽车电子测试领域的先锋,TPT软件始终致力于为用户提供高效、精准、可靠的测试解决方案。新思科技出品的TPT软件迎来了又一次重大更新,最新版本TPT 2024.12将进一步满足汽车行业日益增长的测试需求,推动汽车电子技术的持续革新。基于当前汽车客户的实际需求与痛点,结合最新的技术趋势,对TPT软件进行了全面的优化与升级。从模型故障注入测试到服务器函数替代C代码函数,从更准确的需求链接到P
      北汇信息 2025-03-13 14:43 115浏览
    • DeepSeek自成立之初就散发着大胆创新的气息。明明核心开发团队只有一百多人,却能以惊人的效率实现许多大厂望尘莫及的技术成果,原因不仅在于资金或硬件,而是在于扁平架构携手塑造的蜂窝创新生态。创办人梁文锋多次强调,与其与大厂竞争一时的人才风潮,不如全力培养自家的优质员工,形成不可替代的内部生态。正因这样,他对DeepSeek内部人才体系有着一套别具一格的见解。他十分重视中式教育价值,因而DeepSeek团队几乎清一色都是中国式学霸。许多人来自北大清华,或者在各种数据比赛中多次获奖,可谓百里挑一。
      优思学院 2025-03-13 12:15 121浏览
    • 现代旅游风气盛行,无论国内或国外旅游,导航装置无疑就是最佳的行动导游;在工作使用上也有部分职业(如:外送服务业)需要依靠导航系统的精准,才能将餐点准确无误的送至客户手上。因此手机导航已开始成为现代生活上不可或缺的手机应用之一。「它」是造成产品潜在风险的原因之一外送服务业利用手机导航,通常是使用手机支架固定在机车上,但行进间的机车其环境并不一定适用于安装手机,因行进间所产生的振动可能会影响部分的功能,进而导致受损。您是否曾在新闻报导中看过:有使用者回报在机车上使用手机架导航会造成相机无法开启?苹果
      百佳泰测试实验室 2025-03-13 18:17 124浏览
    • 在海洋监测领域,基于无人艇能够实现高效、实时、自动化的海洋数据采集,从而为海洋环境保护、资源开发等提供有力支持。其中,无人艇的控制算法训练往往需要大量高质量的数据支持。然而,海洋数据采集也面临数据噪声和误差、数据融合与协同和复杂海洋环境适应等诸多挑战,制约着无人艇技术的发展。针对这些挑战,我们探索并推出一套基于多传感器融合的海洋数据采集系统,能够高效地采集和处理海洋环境中的多维度数据,为无人艇的自主航行和控制算法训练提供高质量的数据支持。一、方案架构无人艇要在复杂海上环境中实现自主导航,尤其是完
      康谋 2025-03-13 09:53 118浏览
    • 在追求更快、更稳的无线通信路上,传统射频架构深陷带宽-功耗-成本的“不可能三角”:带宽每翻倍,系统复杂度与功耗增幅远超线性增长。传统方案通过“分立式功放+多级变频链路+JESD204B 接口”的组合试图平衡性能与成本,却难以满足实时性严苛的超大规模 MIMO 通信等场景需求。在此背景下,AXW49 射频开发板以“直采+异构”重构射频范式:基于 AMD Zynq UltraScale+™ RFSoC Gen3XCZU49DR 芯片的 16 通道 14 位 2.5GSPS ADC 与 16
      ALINX 2025-03-13 09:27 85浏览
    • 文/Leon编辑/cc孙聪颖作为全球AI领域的黑马,DeepSeek成功搅乱了中国AI大模型市场的格局。科技大厂们选择合作,接入其模型疯抢用户;而AI独角兽们则陷入两难境地,上演了“Do Or Die”的抉择。其中,有着“大模型六小虎”之称的六家AI独角兽公司(智谱AI、百川智能、月之暗面、MiniMax、阶跃星辰及零一万物),纷纷开始转型:2025年伊始,李开复的零一万物宣布转型,不再追逐超大模型,而是聚焦AI商业化应用;紧接着,消息称百川智能放弃B端金融市场,聚焦AI医疗;月之暗面开始削减K
      华尔街科技眼 2025-03-12 17:37 220浏览
    • 各大Logo更新汇报 | NEW百佳泰为ISO/IEC17025实验室,特为您整理2025年3月各大Logo的最新规格信息。USB™▶ USB Type-C/PD 互操作性MacBook Pro 16英寸(Apple M4 Max 芯片,36GB 内存–1TB SSD–140W USB-C电源适配器)或 MacBook Pro 16英寸(M4 Pro芯片,24GB内存–512 TB SSD–140W USB-C电源适配器),这些型号支持USB4 80Gbps传输速度和 140W EPR功率。需尽
      百佳泰测试实验室 2025-03-13 18:20 121浏览
    • 一、行业背景与用户需求随着健康消费升级,智能眼部按摩仪逐渐成为缓解眼疲劳、改善睡眠的热门产品。用户对这类设备的需求不再局限于基础按摩功能,而是追求更智能化、人性化的体验,例如:语音交互:实时反馈按摩模式、操作提示、安全提醒。环境感知:通过传感器检测佩戴状态、温度、压力等,提升安全性与舒适度。低功耗长续航:适应便携场景,延长设备使用时间。高性价比方案:在控制成本的同时实现功能多样化。针对这些需求,WTV380-8S语音芯片凭借其高性能、多传感器扩展能力及超高性价比,成为眼部按摩仪智能化升级的理想选
      广州唯创电子 2025-03-13 09:26 98浏览
    • 文/杜杰编辑/cc孙聪颖‍主打影像功能的小米15 Ultra手机,成为2025开年的第一款旗舰机型。从发布节奏上来看,小米历代Ultra机型,几乎都选择在开年发布,远远早于其他厂商秋季主力机型的发布时间。这毫无疑问会掀起“Ultra旗舰大战”,今年影像手机将再次被卷上新高度。无意臆断小米是否有意“领跑”一场“军备竞赛”,但各种复杂的情绪难以掩盖。岁岁年年机不同,但将2-3年内记忆中那些关于旗舰机的发布会拼凑起来,会发现,包括小米在内,旗舰机的革新点,除了摄影参数的不同,似乎没什么明显变化。贵为旗
      华尔街科技眼 2025-03-13 12:30 155浏览
    • 曾经听过一个“隐形经理”的故事:有家公司,新人进来后,会惊讶地发现老板几乎从不在办公室。可大家依旧各司其职,还能在关键时刻自发协作,把项目完成得滴水不漏。新员工起初以为老板是“放羊式”管理,结果去茶水间和老员工聊过才发现,这位看似“隐形”的管理者其实“无处不在”,他提前铺好了企业文化、制度和激励机制,让一切运行自如。我的观点很简单:管理者的最高境界就是——“无为而治”。也就是说,你的存在感不需要每天都凸显,但你的思路、愿景、机制早已渗透到组织血液里。为什么呢?因为真正高明的管理,不在于事必躬亲,
      优思学院 2025-03-12 18:24 108浏览
    • 北京时间3月11日,国内领先的二手消费电子产品交易和服务平台万物新生(爱回收)集团(纽交所股票代码:RERE)发布2024财年第四季度和全年业绩报告。财报显示,2024年第四季度万物新生集团总收入48.5亿元,超出业绩指引,同比增长25.2%。单季non-GAAP经营利润1.3亿元(non-GAAP口径,即经调整口径,均不含员工股权激励费用、无形资产摊销及因收购产生的递延成本,下同),并汇报创历史新高的GAAP净利润7742万元,同比增长近27倍。总览全年,万物新生总收入同比增长25.9%达到1
      华尔街科技眼 2025-03-13 12:23 116浏览
    • 引言汽车行业正经历一场巨变。随着电动汽车、高级驾驶辅助系统(ADAS)和自动驾驶技术的普及,电子元件面临的要求从未如此严格。在这些复杂系统的核心,存在着一个看似简单却至关重要的元件——精密电阻。贞光科技代理品牌光颉科技的电阻选型过程,特别是在精度要求高达 0.01% 的薄膜和厚膜技术之间的选择,已成为全球汽车工程师的关键决策点。当几毫欧姆的差异可能影响传感器的灵敏度或控制系统的精确性时,选择正确的电阻不仅仅是满足规格的问题——它关系到车辆在极端条件下的安全性、可靠性和性能。在这份全面指南中,我们
      贞光科技 2025-03-12 17:25 136浏览
    • 一、行业背景与需求痛点智能电子指纹锁作为智能家居的核心入口,近年来市场规模持续增长,用户对产品的功能性、安全性和设计紧凑性提出更高要求:极致空间利用率:锁体内部PCB空间有限,需高度集成化设计。语音交互需求:操作引导(如指纹识别状态、低电量提醒)、安全告警(防撬、试错报警)等语音反馈。智能化扩展能力:集成传感器以增强安全性(如温度监测、防撬检测)和用户体验。成本与可靠性平衡:在复杂环境下确保低功耗、高稳定性,同时控制硬件成本。WTV380-P(QFN32)语音芯片凭借4mm×4mm超小封装、多传
      广州唯创电子 2025-03-13 09:24 112浏览
    我要评论
    0
    0
    点击右上角,分享到朋友圈 我知道啦
    请使用浏览器分享功能 我知道啦