添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

MISAR C是汽车行业C编码标准,由汽车产业软件可靠性协会(MISRA)发布,目的在提升嵌入式系统的安全性和可移植性。针对C++,也有对应标准MISRA C++。本文主要对MISRA C 2012标准进行介绍,扩展对部分条款的理解。

2012发布MISRA C第三版,称为MISRA C:2012。
MISRA C不能100%确保程序不出问题,但能有效预防编程带来的问题。MISRA C具有以下优势:
1)提升可靠性;
2)提升可读性;
3)提升可移植性;
4)提升可维护性;
5)提升安全性。

指示、规则汇总统计:

不可静态检查
Any implementation-defined behaviour on which the output of the
program depends shall be documented and understood

要求。应该用文档记录并了解程序输出依赖的任何实现定义行为。

编译和构建

  • Dir 2.1
  • All source files shall compile without any
    compilation errors
    

    要求。所有源文件必须没有任何编译错误。

    需求可追踪性

  • Dir 3.1
  • All code shall be traceable to documented
    requirements
    

    要求。所有代码应该可以追溯至文件要求。

  • Dir 4.1
  • Run-time failures shall be minimised
    

    要求。运行时故障必须最小化。

  • Dir 4.2
  • All usage of assembly language should be
    documented
    

    建议。所有汇编的使用应当用文档记录。

  • Dir 4.3
  • Assembly language shall be encapsulated and
    isolated
    

    要求。汇编语言必须封装、隔离。

  • Dir 4.4
  • Sections of code should not be 'commented out'
    

    建议。代码部分不应当注释掉。

    例如,不应该用"// ... "或者"/* ... */" 注释代码。而应该用"#ifdef ...#endif"等预编译指令。

  • Dir 4.5
  • Identifiers in the same namespace with overlapping
    visibility should be typographically unambiguous
    

    建议。同一命名空间中,具有重叠可见性的标识符,必须在排版上毫不含糊。

    类似于C++名称遮掩问题。例如,全局变量不要与局部变量重名。

  • Dir 4.6
  • typedefs that indicate size and signedness should
    be used in place of the basic numerical types
    

    建议。指示大小、符号的typedefs(类型定义),应当用来替代基本的数字类型。

    例如,typedef定义的uint32_t,用来替代32位无符号整型。

  • Dir 4.7
  • If a function returns error information, then that
    error information shall be tested
    

    要求。如果一个函数返回错误信息,那么错误信息应答被测试。

  • Dir 4.8
  • If a pointer to a structure or union is never
    dereferenced within a translation unit, then the
    implementation of the object should be hidden
    

    建议。如果一个指向struct或union的指针,在翻译单元内从未被解引用,则应该隐藏该对象的实现。

    例如,结构体A实现如下:

    // A.h
    struct A {
        uint32_t id;
        uint8_t name[32];
    

    如果test.c中,从未对指向A类型对象的指针进行解引用,也就没有访问其成员。此时,在test.c文件中应该使用前向声明,而不应该使用"#include <A.h>"。

    // test.c
    // 正确用法:前向声明
    struct A;
    // include 不必要
    #include <A.h>
    
  • Dir 4.9
  • A function should be used in preference to a
    function-like macro where they are interchangeable
    

    建议。在函数、函数类的宏可以相互替换的地方,应优先使用函数。

    因为函数会在编译期做类型检查,更安全。

  • Dir 4.10
  • Precautions shall be taken in order to prevent the
    contents of a header file being included more than
    

    要求。应当采取预防措施,防止头文件的内容被多次包含。

    通常,使用"#ifndef ... #endif"。也可以使用"#pragma once",不过想要编译器支持。

  • Dir 4.11
  • The validity of values passed to library functions
    shall be checked
    

    要求。传递给库函数的值的有效性,应当被检查。

    因为一些库函数有严格的限制域,需要检查:
    1)许多数学函数(<math.h>中的math函数),如:
    (1)负数不允许传递给sqrt, log函数。
    (2)fmod第二个参数不应为0。

    2)当非小写字母的参数传递给函数toupper时(类似有tolower),一些实现能产生非预期结果。
    3)<ctype.h>中的字符测试函数,在传递无效值时,表现出未定义行为。如,isalnum, isalpha, islower等。
    4)给abs函数传递最值负数时,表现出未定义行为。最小负数值转换成整数值,由于位宽限制,无法正确转换。

  • Dir 4.12
  • Dynamic memory allocation shall not be used
    

    要求。不应该使用动态内存分配。

    例如,不应该使用malloc/free进行动态内存分配。

  • Dir 4.13
  • Functions which are designed to provide operations
    on a resource should be called in an appropriate
    sequence
    

    建议。设计用来提供操作资源的函数,应当以合适的序列进行调用。

    例如,某个硬件模块的操作,应当遵循一定顺序,以符合硬件资源特性。

    规则(Rules)

    标准C环境

  • Rule 1.1
  • The program shall contain no violations of the
    standard C syntax and constraints, and shall not
    exceed the implementation's translation limits
    

    要求。程序不得违反标准C语法和约束,不应超出实现的翻译限制。

    什么意思?
    程序必须只使用C语言特性及其库。除非使用语言扩展,否则程序不应:
    1)包含任何违反本标准中描述的语言语法行为;
    2)包含任何违反本标准规定的限制行为。

    语法行为:不支持写const变量。
    语言扩展:一些C90编译器提供__inline关键字声明inline函数。许多编译器支持使用一些关键字定义对象位置,如__zpage, __near, __far。

  • Rule 1.2
  • Language extensions should not be used
    

    建议。语言扩展不应使用。

    编译器通常会提供一些标准C(ISO 标准C)以外的语言特性。GCC可以用'-pedantic'选项,打印使用了这些特性的警告信息。

    例如,GNU C提供的语言扩展:
    1)表达式中的声明

    // case 1: 函数参数表达式
    ({ int y = foo(); int z;
        if (y > 0) z = y;
        else z = -y;
        z; })
    // case 2
    #define maxint(a,b) \
        ({int _a = (a), _b = (b); _a > _b ? _a : _b; })
    // case 3: C++中
    ({a;}).Foo()
    

    2)本地声明标签
    local作用域内的标签声明。标签通常搭配goto使用。
    例如,下面的found就是local标签:

    #define SEARCH(value, array, target)             \
    do {                                             \
      __label__ found;                               \
      typeof (target) _SEARCH_target = (target);     \
      typeof (*(array)) *_SEARCH_array = (array);    \
      int i, j;                                      \
      int value;                                     \
      for (i = 0; i < max; i++)                      \
        for (j = 0; j < max; j++)                    \
          if (_SEARCH_array[i][j] == _SEARCH_target) \
            { (value) = i; goto found; }             \
          (value) = -1;                              \
          found:;                                    \
    } while (0)
    

    3)标签作为值
    可以用单目操作符"&&",得到在当前函数内定义的标签地址。

    void *ptr;
    ptr = &&foo;
    goto *ptr; // 跳转到foo函数
    

    4)嵌套函数
    一个函数嵌套定义在另一个函数内。

    foo (double a, double b) {
        double square(double z) { return z * z; } // square是嵌套函数
        return square (a) + square(b);
    

    5)构建函数调用
    使用编译器内置函数,记录函数收到的参数,调用另一个具有相同参数的函数,而无需知道参数的数值或类型。

    6)参考typeof定义的类型
    一个类型,无需知道具体类型,可以通过数据推导出类型。

    typeof (x[0](1))
    typeof (int*)
    #define max(a,b) \
        ({ typeof(a) _a = (a); \
           typeof(b) _b = (b); \
           _a > _b ? _a : _b; })
    

    7)带省略操作的条件表达式
    三元表达式的条件表达式,中间操作数可省略。

    x ? : y
    x ? x : y
    

    8)128-bit整型

    signed __int128   // 有符号128-bit整型
    unsigned __int128 // 无符号128-bit整型
    

    9)双字整型
    C99支持数据类型long long int,对应至少64bit宽度,而在C90中,GCC将其作为扩展实现。

    10)复数

    _Complex // 关键字
    __complex__ // 旧版本GNU关键字
    

    等等GNU扩展C语言特性。

  • Rule 1.3
  • There shall be no occurrence of undefined or
    critical unspecified behaviour
    

    要求。不应出现未定义或关键的未指定行为。

    未使用代码(Unused code)

  • Rule 2.1
  • A project shall not contain unreachable code
    

    要求。项目不应包含不可达代码。

    void foo() {
        int a = 1;
        if (a != 0) {
        else { // 这里else表达式不可达
    
  • Rule 2.2
  • There shall be no dead code
    

    要求。不应包含死代码。

    什么是死代码?
    任何已执行,但其擅长不会影响程序行为的代码,都会构成死代码。而由语言扩展引入的操作被认为总是对程序行为有影响的。
    例如,下面函数就是死代码,因为不会影响程序行为。

    extern volatile uint16_t v;
    extern char *p;
    void f ( void )
      uint16_t x;
      ( void ) v;    /* Compliant - v is accessed for its side effect
                      * and the cast to void is permitted
                      * by exception  */
      ( int32_t ) v; /* Non-compliant - the cast operator is dead */ // 死代码
      v >> 3;        /* Non-compliant - the >> operator is dead */   // 死代码
      x = 3;         /* Non-compliant - the = operator is dead       // 死代码
                      * - x is not subsequently read */
      *p++;          /* Non-compliant - result of * operator is not used */
      (*p )++;       /* Compliant - *p is incremented */
    __asm("NOP"); // __asm是语言扩展关键字, 因此并非死代码
    

    另一种情况,函数并非死代码,但函数调用是死代码。

    void g ( void ) // g是空函数, 非死代码
    /* Compliant - there are no operations in this function */
    void h ( void ) // 死代码
    
  • Rule 2.3
  • A project should not contain unused type
    declarations
    

    建议。项目不应包含未使用类型声明。

    如果一个类型定义但未使用,审核人不清楚该类型是否冗余,或者遗留的未使用错误。例如,

    int16_t unusedtype ( void )
        typedef int16_t local_Type; /* Non-compliant */
        return 67;
    
  • Rule 2.4
  • A project should not contain unused tag declarations
    

    建议。项目不应包含未使用标记声明。

    // enum state未使用
    void unusedtag ( void )
        enum state { S_init, S_run, S_sleep }; /* Non-compliant */
    // 没有使用struct record_t, 不建议声明
    typedef struct record_t /* Non-compliant */
        uint16_t key;
        uint16_t val;
    } record1_t;
    // 使用了record2_t, 推荐声明
    typedef struct /* Compliant */
        uint16_t key;
        uint16_t val;
    } record2_t;
    
  • Rule 2.5
  • A project should not contain unused macro
    declarations
    

    建议。项目不应包含未使用宏定义声明。

  • Rule 2.6
  • A function should not contain unused l abel declarations
    

    建议。函数不应包含未使用标签声明。

  • Rule 2.7
  • There should be no unused parameters in functions
    

    建议。函数中,不应有未使用参数。

    如果确实定义了未使用的参数,为了确保函数兼容性,可以形参明省略

    void withunusedpara ( uint1 6_t *para1, int16_t unusedpara) /* Non-compliant - unused */
        *para1 = 42U;
    void withunusedpara ( uint1 6_t *para1, int16_t) // 推荐做法, 删去形参名
    { ... }
    

    注释(Comments)

  • Rule 3.1
  • The character sequences /* an d // shal l not be used within a
    comment
    

    要求。字符序列 /*和//不应在中使用。
    例如,下面行为不符合该规则:

    x = y // /*
          // */
    
  • Rule 3.2
  • Line-splicing shall not be used in // comments
    

    要求。不得在注释//中使用续行符('')。

    例如,下面行为不符合该规则:

    extern bool_t b;
    void f ( void )
        uint16_t x = 0; // comment \
        if ( b )
            ++x; /* This is always executed */
    

    字符集和词汇约定(Character sets and lexical conventions)

  • Rule 4.1
  • Octal and hexadecimal escape sequences shall be terminated
    

    要求。8进制和16进制高级转义序列应终止。

    如果8进制或16进制转义序列后跟其他转义序列,可能出现混淆。如,字符常量'\x1f'由1个字符组成(表示ASCII 16进制值为1f的单元分隔符US),而'\x1g'由2个字符'\x1'(表示ASCII 16进制值为1的标题开始SOH)和'g'组成。

    // 16进制转义序列, 以\x开头
    const char *s1 = "\x41g"; /* Non-compliant */
    const char *s2 = "\x41" "g"; /* Compliant - terminated by end of literal */
    const char *s3 = "\x41\x67"; /* Compliant - terminated by another escape */
    // 8进制转义序列, 以\1开头
    int c1 = '\141t'; /* Non-compliant */
    int c2 = '\141\t'; /* Compliant - terminated by another escape */
    
  • Rule 4.2
  • Trigraphs should not be used
    

    建议。三字母词不应使用。

    三字母词(Trigraphs)由两个问号的序列,跟着一个特殊的第三字符。例如,??- 表示~(波浪线),??) 表示 a]。它们可能与两个问号的其他用法混淆。
    比如,字符串"(Date should be in the form ??-??-??)"会被编译器解释为"(Date should be in the form ~~]"。
    Trigraphs会在预处理阶段被替换。

    标识符(Identifiers)

  • Rule 5.1
  • External identifiers shall be distinct
    

    要求。外部标识符应该不同。

  • Rule 5.2
  • Identifiers declared in the same scope and name space shall be
    distinct
    

    要求。声明在同一作用域和命名空间的标识符,应该是不同的。

  • Rule 5.3
  • An identifier declared in an inner scope shall not
    hide an identifier declared in an outer scope
    

    要求。在内部作用域中声明的标识符,不应隐藏外部作用域中声明的标识符。

    // 外部作用域中声明的标识符
    extern int32_t engine_exhaust_gas_temperature_raw;
    // 内部作用域中声明的标识符
    static int32_t engine_exhaust_gas_temperature_scaled; /* Non-compliant */
    void f ( void )
        /* 1234567890123456789012345678901********* Characters */
        int32_t engine_exhaust_gas_temperature_local; /* Compliant */
    
  • Rule 5.4
  • Macro identifiers shall be distinct
    

    要求。宏定义标识符应该不同。

  • Rule 5.5
  • Identifiers shall be distinct from macro names
    

    要求。标识符应与宏定义名称不同。

  • Rule 5.6
  • A typedef name shall be a unique identifier
    

    要求。typedef类型名应该是独有的标识符。

  • Rule 5.7
  • A tag name shall be a unique identifier
    

    要求。标记名应该是独有的标识符。

    例如,不应有与struct名称 同名的union类型;也不应该定义重名的标记名。

  • Rule 5.8
  • Identifiers that define objects or functions with
    external linkage shall be unique
    

    要求。使用外部链定义对象或函数的标识符应该唯一。

    不同源文件,不要存在标识符重名的情况。

  • Rule 5.9
  • Identifiers that define objects or functions with
    internal linkage should be unique
    

    建议。使用内部链接定义的对象或函数应该唯一。

    同一源文件,不要存在标识符重名的情况。

    类型(types)

  • Rule 6.1
  • Bit-fields shall only be declared with an appropriate
    

    要求。位域只能用适当的类型声明。

    对于C90,适当的位域类型:unsigned int 或 signed int。
    对于C99,适当的位域类型:
    1)unsigned int 或 signed int;
    2)实现所允许的另一个显式有符号或显式无符号整型;
    3)_Bool。
    注意:允许使用typedef来指定适当的类型。

    反例:C90中,enum, short, char等不是适当的位域类型;而C99中,这些由整型实现,是适当的位域类型。

  • Rule 6.2
  • Single-bit named bit fields shall not be of a signed
    

    要求。单比特命名位字段不应为带符号类型。

    注意:该规则不适用于匿名位域。

    文字和常量(Literals and constants)

  • Rule 7.1
  • Octal constants shall not be used
    

    要求。不应使用8进制常数。

    因为很容易跟10进制常数混淆,比如52(10进制),052(8进制,对应10进制42)。

  • Rule 7.2
  • A "u" or "U" suffix shall be applied to all integer
    constants that are represented in an unsigned type
    

    要求。"u"或"U"后缀应该应用到整型常量,代表无符号类型。

  • Rule 7.3
  • The lowercase character 'l' shall not be used in a
    literal suffix
    

    要求。小写字母'l'不应用于文字后缀。

    因为很容易跟数字'1'混淆。

  • Rule 7.4
  • A string literal shall not be assigned to an object
    unless the object's type is “pointer to const-qualified char”
    

    要求。字符串文字不应赋值给对象,除非对象的类型是“指向常量限定字符的指针”。

    因为字符串文字是常量,传递给non-const对象的话,用户修改内容可能会导致异常。

    char *s = "string";  // 不符合该规则
    const volatilechar *p = "string"; // 符合
    extern void f1(char *s1);
    extern void f2(const char *s2);
    void g(void) {
        f1("string"); // 不符合
        f2("string"); // 符合
    char *name1(void) { // 不符合
        return ("MISRA");
    const char *name2(void) { // 符合
        return ("MISRA");
    

    声明和定义( Declarations and definitions)

  • Rule 8.1
  • Types shall be explicitly specified
    

    要求。类型应明确指定。

    因为C90标准允许在特定情形下,省略类型,此时,int类型为隐式指定。可能使用隐式int的情况示例如下:
    1)对象声明;
    2)参数声明;
    3)成员声明;
    4)typedef 声明;
    5)函数返回类型。

    extern x; // 隐式int, 不符合该条规则
    extern int16_t x; // 显式类型
    const y;  // 隐式int
    const uint16_t y; // 显式类型
    extern f(void); // 隐式int
    extern int16_t f(void); // 显式类型
    extern void g(char c, const k); // k 是隐式int类型
    extern void g(char c, const int16_t k); // 显示类型
    struct str {
        int16_t x; // 显式类型
        const y;   // 隐式int
    

    省略明确的类型可能导致混淆。例如:

    extern void g(char c, const k); // k是const int, 但用户可能期望是const char
    
  • Rule 8.2
  • Function types shall be in prototype form with
    named parameters
    

    要求。函数类型应该为带命名参数的原型。

    为了避免混淆,或者不一致。例如:

    extern int16_t func1(int16_t n); // 符合
    extern void func2(int16_t); // 不符合
    static int16_t func3();     // 不符合
    static int16_t func4(void); // 符合
    
  • Rule 8.3
  • All declarations of an object or function shall use
    the same names and type qualifiers
    

    要求。所有对象或函数的声明应使用相同的名字或类型限定符。

    相同基本类型的兼容版本可以互换,如int, signed, signed int 都是等价的。而const, non-const则不可以互换。

  • Rule 8.4
  • A compatible declaration shall be visible when an
    object or function with external linkage is defined
    

    要求。当对象或函数由外部链接定义时,兼容声明应当是可见的。

    定义外部对象或函数时,有一个定义,必须对应一个声明。

    extern int16_t count;
           int16_t count = 0; /* Compliant */ // 符合该条规则
    // 不符合, 因为定义之前没有声明
    extern uint16_t speed = 6000u; /* Non-compliant - no declaration prior to this definition */ 
    // 不符合, 因为定义之前没有声明
    uint8_t pressure = 101u; /* Non-compliant - no declaration prior to this definition */
    
  • Rule 8.5
  • An external object or function shall be declared
    once in one and only one file
    

    要求。外部对象或函数应在一个且仅在一个文件中声明。

    也就是说,每个外部对象或函数,应当仅在一个.h文件中声明一次。

  • Rule 8.6
  • An identifier with external linkage shall have exactly
    one external definition
    

    要求。外部链接的标识符应有一个外部定义。

    包含2点:1)标识符必须有定义;2)定义只能有一个。

  • Rule 8.7
  • Functions and objects should not be defined with
    external linkage if they are referenced in only one
    

    建议。如果函数和对象仅在一个翻译单元中引用,则不应由外部链接定义。

    这是为了限制对象可见性。

  • Rule 8.8
  • The static storage class specifier shall be used in
    all declarations of objects and functions that have
    internal linkage
    

    要求。静态存储类说明符 应该用于所有内部链接的对象和函数的声明。

  • Rule 8.9
  • An object should be defined at block scope if its
    identifier only appears in a single function
    

    建议。如果对象的标识符仅出现在单个函数中,则应在块范围内定义该对象。

    减少对象的可见性。

  • Rule 8.10
  • An inline function shall be declared with the static
    storage class
    

    要求。内联函数应与静态存储类一同声明。

    内联函数声明为外部链接,但没有在同一翻译单元内定义,会导致未定义行为。
    调用外部链接的内联函数,可能调用外部函数的定义,或者使用内联定义,这会影响执行速度。

    注意:可通过内联函数置于将头文件,使得内联函数在多个翻译单元内可用。

  • Rule 8.11
  • When an array with external linkage is declared, its
    size should be explicitly specified
    

    要求。当外部链接的数组声明时,它的尺寸应明确指定。

    该规则仅应用于非定义的声明。声明中明确数组尺寸,便于检查一致性,以及边界检查。例如,

    extern int32_t array1[ 10 ]; /* Compliant */
    extern int32_t array2[ ];    /* Non-compliant */
    
  • Rule 8.12
  • Within an enumerator list, the value of an implicitly specified
    enumeration constant shall be unique
    

    要求。在枚举列表中,隐式指定枚举常量值是唯一的。

    未指定值的枚举成员默认递增,注意不能与其他成员同值。

  • Rule 8.13
  • A pointer should point to a const-qualified type
    whenever possible
    

    建议。指针应尽可能指向常量限定类型。

  • Rule 8.14
  • The restrict type qualifier shall not be used
    

    要求。restrict类型限定符不应使用。

    C中restrict关键字用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的防晒修改该对象的内容。当使用restrict类型限定符时,可能会改善由编译器生成的代码效率。但使用restrict同时,也要求程序员必须确保两个指针所指内存区域没有重叠。

    初始化(Initialization)

  • Rule 9.1
  • The value of an object with automatic storage
    duration shall not be read before it has been set
    

    强制。自动变量在设置之前不允许读取。

  • Rule 9.2
  • The initializer for an aggregate or union shall be
    enclosed in braces
    

    要求。聚合体或联合体的初值应该包含在大括号中。

    聚合体是指数组(array)或类(class)或结构体(struct)。

    注意:{ 0 }形式的初始化器,可以设置所有值为0,而无需嵌套括号。

    int16_t y[ 3 ][ 2 ] = { 1, 2, 0, 0, 5, 6 }; /* Non-compliant */
    int16_t y[ 3 ][ 2 ] = { { 1, 2 }, { 0 }, { 5, 6 } }; /* Compliant */
    int16_t y[ 3 ][ 2 ] = { { 1, 2 }, { 0, 0 }, { 5, 6 } }; /* Compliant */
    
  • Rule 9.3
  • Arrays shall not be partially initialized
    

    要求。数组不应部分初始化。

    必须为每个数组元素设定初值。

    // 符合这条规则
    int16_t a1[ 5 ] = { -5, -4, -3, -2, -1 };
    // 符合
    int16_t a2[ 5 ] = { [ 0 ] = -5, [ 1 ] = -4, [ 2 ] = -3,
                        [ 3 ] = -2, [ 4 ] = -1 };
    // 不符合, 因为第2号元素重复指定值, 第3号元素未指定值
    int16_t a3[ 5 ] = { [ 0 ] = -5, [ 1 ] = -4, [ 2 ] = -3,
                        [ 2 ] = -2, [ 4 ] = -1 };
    
  • Rule 9.4
  • An element of an object shall not be initialised
    more than once
    

    要求。对象的元素不应初始化超过一次。

  • Rule 9.5
  • Where designated initialisers are used to initialize
    an array object the size of the array shall be
    specified explicitly
    

    要求。如果指定的初始化器用于初始化数组对象,那么应明确指定数组的大小。

    // 不符合, 因为用初始化器初始化数组时, 没有明确指定大小
    int a1[ ] = { [ 0 ] = 1 };
    // 符合
    int a2[ 10 ] = { [ 0 ] = 1 };
    

    基本类型模型(The essential type model)

  • Rule 10.1
  • Operands shall not be of an inappropriate essential
    

    要求。操作数不应具有不恰当的基本类型。

    算术操作数的基本类型类别:

    Opeartor Operand Boolean character signed unsigned floating 1.对这些操作数使用floating类型,是违反约束的。
    2.本质上Boolean类型的表达式,应该用于操作数解释为Boolean值的地方。
    3.本质上Boolean类型的操作数,不应用于操作数解释为数值的地方。
    4.本质上character类型的操作数,不应用于操作数解释为数值的地方。字符数据的数值是由实现定义的。
    5.本质上enum类型的操作数,不应用于算术操作,因为enum对象使用整型定义实现。涉及枚举对象的操作,可能产生意外类型的结果。注意匿名枚举中的枚举常量,本质上具有带符号类型。
    6.移位和逐位操作仅对本质上无符号的操作数执行。本质上有符号类型的使用产生的数值,是由定义实现的。
    7.移位运算的右侧操作符,应该是本质上无符号类型,以确保负移位不会刀子划未定义行为。
    8.本质上有符号类型的操作数,不应用于一元减号运算符的操作数,因为结果的符号性由实现的int大小决定。

  • Rule 10.2
  • Expressions of essentially character type shall not
    be used inappropriately in addition and subtraction operations
    

    要求。本质上为字符类型的表达式,不应用在不正确的加法和减法运算中。

    当数据不代表数值时,带字符类型的表达式不能用于算术运算。然而有些情况是允许字符数据的运算的。
    1)2个字符类型的操作数的减法,可用于在数字'0'到'9'和相应的序数之间转换。
    2)字符类型和无符号类型的加法,可能用于序数到相应数字范围'0'至'9的转换。
    3)从字符类型到无符号类型底减法,可能用于小写转大写。

    '0' + u8a // 符合: 无符号整型u8a转换为数字字符
    s8a + '0' // 符合:有符号整型s8a转换为数字字符
    cha - '0' // 符合:字符cha转换为序数
    '0' - s8a // 符合:有符号整型-cha转换为数字字符
    s16a - 'a' // 不符合
    '0' + f32a // 不符合,f32a是浮点型
    cha + ':'  // 不符合
    cha - ena  // 不符合
    
  • Rule 10.3
  • The value of an expression shall not be assigned to
    an object with a narrower essential type or of a
    different essential type category
    

    要求。表达式的值不应赋值给本质类型较窄 或 不同的基本类型 的对象。

    宽数字赋值给窄数字,会发生截断。

  • Rule 10.4
  • Both operands of an operator in which the usual
    arithmetic conversions are performed shall have
    the same essential type category
    

    要求。通常的算术转换时的运算符的2个操作数,应具有相同点基本类型类别。

  • Rule 10.5
  • The value of an expression should not be cast to
    an inappropriate essential type
    

    建议。表达式的值不应转型为不恰当的本质类型。

  • Rule 10.6
  • The value of a composite expression shall not be
    assigned to an object with wider essential type
    

    要求。符合表达式的值不应赋值给具有更宽基本类型的对象。

    可以让运算过程提升数值宽度,但不要在赋值时提升。

    // 符合
    u16c = u16a + u16b;         /* Same essential type */
    u32a = ( uint32_t ) u16a + u16b; /* Cast causes addition in uint32_t */
    // 不符合
    u32a = u16a + u16b;         /* Implicit conversion on assignment */
    use_uint32 ( u16a + u16b ); /* Implicit conversion of fn argument */
    
  • Rule 10.7
  • If a composite expression is used as one operand of an operator 
    in which the usual arithmetic conversions are performed then the 
    other operand shall not have wider essential type
    

    要求。如果一个复合表达式被用作执行常用算术转换的运算符的一个操作数,另一个运算数不应具有更宽的本质类型。

    u32a * u16a + u16b      /* No comp osite conversion */
    ( u32a * u16a ) + u16b  /* No composite conversion */
    u32a * ( ( uint32_t ) u16a + u16b ) /* Both operands of * have same essential type */
    u32a += ( u32b + u16b ) /* No composite conversion */
    u32a * ( u16a + u16b )  /* Implicit conversion of ( u16a + u16b ) */
    u32a += ( u16a + u16b ) /* Implicit conversion of ( u16a + u16b ) */
    
  • Rule 10.8
  • The value of a composite expression shall not be
    cast to a different essential type category or a wider
    essential type
    

    要求。复合表达式的值不应转型为不同本质类型类别或一更宽的本质类型。

    指针类型转换(Pointer type conversions)

  • Rule 11.1
  • Conversions shall not be performed between a
    pointer to a function and any other type
    

    要求。转换不应在函数指针和其他类型指针之间进行。

  • Rule 11.2
  • Conversions shall not be performed between a
    pointer to incomplete and any other type
    

    要求。转换不应在不完整类型和其他类型指针之间进行。

    不完整类型指:
    1)void类型
    2)未知大小数组
    3)具有不完整类型元素的数组
    4)未定义的结构体,联合体,或枚举(只有声明)
    5)指向已声明但未定义的类的指针
    6)声明但未定义的类

    异常情况:
    1)null指针产常量能转换为指向不完整类型的指针。
    2)指向不完整类型的指针,能转换为void。

  • Rule 11.3
  • A cast shall not be performed between a pointer to
    object type and a pointer to a different object type
    

    要求。转型不应在指向不同对象类型的指针之间进行。

    uint8_t *p1;
    uint32_t *p2;
    /* Non-compliant - possible incompatible alignment */
    p2 = ( uint32_t * ) p1; // 不符合
    extern uint32_t read_value ( void );
    extern void print ( uint32_t n );
    void f ( void )
        uint32_t u = read_value ( );
        uint16_t *hi_p = ( uint16_t * ) &u; /* Non-compliant even though probably correctly aligned */ // 不符合
        *hi_p = 0;   /* Attempt to clear high 16-bits on big-endian machine */
        print ( u ); /* Line above may appear not to have been performed */
    
  • Rule 11.4
  • A conversion should not be performed between a
    pointer to object and an integer type
    

    建议。转型不应在指向对象的指针和整型之间进行。

  • Rule 11.5
  • A conversion should not be performed from pointer
    to void into pointer to object
    

    建议。不应将指向void的指针转型为指向对象的指针。

    反过来可以:能将指向对象的指针转型为void指针。

  • Rule 11.6
  • A cast shall not be performed between pointer to
    void and an arithmetic type
    

    要求。转型不应在void指针和算术类型之间进行。

    void * p;
    uint32_t u;
    /* Non-compliant - implementation-defined */
    p = ( void * ) 0x1234u;
    /* Non-compliant - undefined */
    p = ( void * ) 1024.0f;
    /* Non-compliant - implementation-defined */
    u = ( uint32_t ) p;
    
  • Rule 11.7
  • A cast shall not be performed between pointer to
    object and a non-integer arithmetic type
    

    要求。转型不应在指向对象的指针和非整型算术类型之间进行。

  • Rule 11.8
  • A cast shall not remove any const or volatile
    qualification from the type pointed to by a pointer
    

    要求。转型不应移除来自指针指向的类型的任何const 或 volatile限定符。

  • Rule 11.9
  • The macro NULL shall be the only permitted form
    of integer null pointer constant
    

    要求。NULL宏是唯一允许的整型空指针常量的形式。

    表达式(Expressions)

  • Rule 12.1
  • The precedence of operators within expressions should be made
    explicit
    

    建议。应明确表达式中运算符的优先级。

    a[ i ]->n;     /* Compliant - no need to write ( a[ i ] )->n */
    *p++;          /* Compliant - no need to write *( p++ ) */
    sizeof x + y;  /* Non-compliant - write either sizeof ( x ) + y
                    * or sizeof ( x + y ) */
    
  • Rule 12.2
  • The right hand operand of a shift operator shall lie in the range zero 
    to one less than the width in bits of the essential type of the left hand
    operand
    

    要求。移位运算符的右手操作数应在0~1之间,比左操作数的基本类型的位宽小。

  • Rule 12.3
  • The comma operator should not be used
    

    建议。逗号操作符不应使用。

  • Rule 12.4
  • Evaluation of constant expressions should not lead
    to unsigned integer wrap-around
    

    建议。常量表达式的值计算不应导致无符号整型环绕(溢出)。

    无符号整型表达式没有严格的的溢出,取而代之的是环绕(wrap-around)。

    例如,下面BASE所指数可能是16bit:

    #define BASE 65024u
    switch ( x )
        case BASE + 0u: // 符合
            f ( );
            break;
        case BASE + 1u: // 符合
            g ( );
            break;
        case BASE + 512u: // 不符合,环绕到0
            h ( );
            break;
    

    副作用(Side effects)

  • Rule 13.1
  • Initialiser lists shall not contain persistent side
    effects
    

    要求。初始化列表不应包含持久副作用。

    C90限制聚合类型的初始化器仅包含常量。但C99允许初值包含运行时计算的表达式,也允许作为匿名初始化对象的复合文字。初始化器列表中表达式求值过程中,副作用发生的顺序锁未指定的,因此,如果这些副作用持续存在,那么初始化的行为不可预测。

    volatile u int16_t v1;
    void f ( void )
        /* Non-compliant - volatile access is persistent side effect */
        uint16_t a[ 2 ] = { v1, 0 };
    void g ( uint16_t x, uint16_t y )
        /* Compliant - no side effects */
        uint16_t a[ 2 ] = { x + y, x - y };
    uint16_t x = 0u;
    extern void p ( uint16_t a[ 2 ] );
    void h ( void )
        /* Non-compliant - two side effects */
        p ( ( uint16_t[ 2 ] ) { x++, x++ } );
    
  • Rule 13.2
  • The value of an expression and its persistent side
    effects shall be the same under all permitted
    evaluation orders
    

    要求。表达式的值和它的持久副作用,应在所有允许的计算顺序情况下相同。

  • Rule 13.3
  • A full expression containing an increment (++) or
    decrement (--) operator should have no other
    potential side effects other than that caused by the
    increment or decrement operator
    

    建议。保护自增或自减运算符的完整表达式,除了由自增或自减运算符引起的副作用外,不应有其他潜在的副作用。

  • Rule 13.4
  • The result of an assignment operator should not be
    

    建议。赋值运算符的结果不应使用。

  • Rule 13.5
  • The right hand operand of a logical && or ||
    operator shall not contain persistent side effects
    

    要求。逻辑运算符 && 或 || 运算符不应包含持久副作用。

  • Rule 13.6
  •