规范原则

  • 朴实,避免晦涩语法
  • 严谨,逻辑反复思考
  • 简约,命名简洁、代码精炼
  • 性能,通过算法、编译器、硬件进行优化

目录结构

  • 原则

    整个工程按照功能模块划分子目录,每个子目录再划分头文件和源文件目录,以便架构清晰、易懂。

  • 示例

    在比较复杂的项目时,建议目录结构如下:

    https://img.alicdn.com/tfs/TB1G7lQdwMPMeJjy1XcXXXpppXa-432-281.png

    在项目相对简单时,建议目录结构如下:

    https://img.alicdn.com/tfs/TB1r5dIdwoQMeJjy1XaXXcSsFXa-461-217.png

系统组件

目录设计

  • 原则

    目录的命名能准确描述模块的基本功能,建议用小写字母且不含下划线、点等特殊符号;

    目录必须放于相包含的父目录之下,并需要明确与其他目录间的耦合性。

  • 示例

    • kernel:系统内核部分;
    • libs:依赖库;
    • mqtt:mqtt协议库;
    • external/mbedtls:位于第三方库目录的mbedtls库

依赖关系

  • 原则

    新添加组件往往依赖于系统原有组件,必须以最小耦合的方式明确所直接依赖的组件。

  • 示例

头文件

文件命名

  • 原则

    头文件命名能准确描述文件所包含的模块内容,达到通俗、易懂的目的。

  • 示例

版权声明(阿里集团内部)

  • 原则

    项目内的所有头文件(除引入的第三方库)版权声明一致,并且在文件内顶格注释。

  • 示例

    如AliOS版权声明如下:

    /*
     * Copyright (C) 2015-2017 Alibaba Group Holding Limited
     */
    

引用保护

  • 原则

    项目内的所有头文件采用#define宏防止多重包含,命名格式为:

    XXX_FILENAME_H

    Note

    为了保证宏定义的惟一性,建议“XXX”命名为头文件目录名、路径名或有意义的其他符号(如“k”: kernel)。

  • 示例

    如下图,在版权声明后空一行,且在“#endif”后空一行且添加注释(注释内容两边各留一个空格且采用“/* */”格式)。

    /*
     * Copyright (C) 2015-2017 Alibaba Group Holding Limited
     */
    
    #ifndef K_ERR_H
    #define K_ERR_H
    
    #endif /* K_ERR_H */
    

引用方式

  • 原则

    系统头文件和第三方库通过“<.h>”方式引用,其他文件通过“.h”引用。

  • 示例

函数定义

  • 原则

    由于C++编译器在编译时与C编译器命名不同,为了保证C++代码能够正确调用C接口, 建议在C语言接口声明时采用extern的方式指示编译器按C语言去编译。

  • 示例

    如下图,在extern的声明代码上下各添加一行空行。

    /*
     * Copyright (C) 2015-2017 Alibaba Group Holding Limited
     */
    
    #ifndef K_ERR_H
    #define K_ERR_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* K_ERR_H */
    

源文件

文件命名

  • 原则

    源文件命名能准确描述文件所包含的模块内容,达到通俗、易懂的目的。

  • 示例

版权声明

  • 原则

    项目内的所有头文件(除引入的第三方库)版权声明一致,并且在文件内顶格注释。

  • 示例

    如AliOS版权声明如下:

    /*
     * Copyright (C) 2015-2017 Alibaba Group Holding Limited
     */
    

头文件依赖

  • 原则

    在源文件中引入头文件时采用如下顺序:C库、C++库、其他第三方库、项目内头文件, 同一类型的头文件采用字母顺序排列,避免引入冗余头文件。

  • 示例

函数定义

  • 原则

    源文件中函数的定义顺序严格按照头文件中声明的顺序排列,且内部函数添加static关键字,比较短的函数还需添加inline关键字。 对外函数必须进行参数检查,内部函数不做检查参数要求。返回值必须类型匹配。

  • 示例

命名风格

原则

  • 统一,相同含义的单词、命名方式项目内统一。
  • 准确,采用计算机领域常用单词。
  • 风格,采用字符加下划线的方式(不使用驼峰风格)

缩写

类型命名

  • 原则

    数据结构类型以“_t”结尾,枚举变量类型以“_e”结尾,回调函数类型以“_cb”结尾;或所有类型以“_t”作为后缀。字符均小写。

  • 示例

    typedef int (*xxx0_cb)(...);
    
    typedef struct {
        ...
    } xxx1_t;
    
    typedef struct xxx2_s {
        struct xxx2_s *x;
        ...
    } xxx2_t;
    
    typedef enum {
        ...
    } xxx3_e;
    

变量命名

  • 原则

    全局变量以“g_”作为前缀,内部变量尽量简洁,每个变量的字段不超过5个,字符均小写。

  • 示例

常量命名

  • 原则

    常量命名各字段必须大写,且字段数不超过5个。

  • 示例

函数命名

  • 原则

    对外的函数建议采用统一前缀,如“aos_”,源文件内部函数建议以“_”作为前缀,且命名字段数不超过5个,字符均小写。

  • 示例

枚举命名

  • 原则

    枚举命名各字段必须大写,且字段数`不超过5个`。

  • 示例

宏命名

  • 原则

    宏命名各字段必须大写,且字段数`不超过5个`。

  • 示例

注释方式

注释风格

  • 原则

    统一采用“/**/”方式注释,且注释内容左右各留一个空格,如“/* message */”;注释必须使用英文。

  • 示例

函数注释

  • 原则

    对外接口按doxgen要求的格式书写,且注释和函数定义间不留空行; 对内接口在需要时添加注释,注释位于定义前。

  • 示例

变量注释

  • 原则

    注释在变量定义后,在仅有一个需要注释的变量时与分号之间只留一个空格; 若多行变量定义均需注释,则最长变量定义留一个空格,其他注释与其对齐。

  • 示例

编译宏注释

  • 原则

    注释在宏定义后,在仅有一个需要注释的宏时与分号之间只留一个空格; 若多行宏定义均需注释,则最长宏定义后留一个空格,其他注释与其对齐。

  • 示例

所有的缩进必须使用空格(除makefile外不允许使用tab),且以`四个空格`为单位进行缩进。 嵌套的编译条件判断顶格写,且需要在“#endif”后的注释中备注属于哪个判断条件。
  • 示例

    #ifdef XXX
    int func1(void)
    {
        ...
    #ifdef XX
        func();
    #endif /* XX */
        ...
    }
    #endif /* XXX */
    

空行

  • 原则

    在所有需要空行的地方仅空一行,如版权声明与代码之间,不同类型的头文件引用之间,函数定义之间等。

  • 示例

变量声明

  • 原则

    全局变量的声明必须放在相关头文件中,且单个声明的类型和变量之间留一个空格,多个声明时按最长的留一个空格对齐。

  • 示例

    extern int   g_x_x;       /* ... */
    extern int   g_xx_xx;     /* ... */
    extern long  g_xxx_xxx;   /* ... */
    extern char *g_xxxx_xxxx; /* ... */
    

变量定义

  • 原则

    全局变量的定义必须放在相关的源文件中,且在头文件和函数定义之间; 局部变量的定义必须放在函数作用域的最开始处; 变量定义的类型和变量之间(或者“*”)留一个空格,当有多个变量时按照最长原则对齐。

  • 示例

    Note

    上图中描述了四种情况下的对齐方式

结构体声明

  • 原则

    在头文件中引用其他项目内头文件的结构体定义时采用extern进行声明,不引入相关头文件。

  • 示例

结构体定义

  • 原则

    结构体定义放在相关头文件中,并且在函数声明前,结构体定义前后需要各空一行。

  • 示例

函数声明

  • 原则

    对外函数的声明必须放在头文件中,且在头文件中按照关联性排序; 当每行超过`80列`时需将参数移到下一行且与上一行参数首字符对齐; 函数内紧邻左右大括号的参数不需要空格,但参数之间需要留一个空格; 仅一个函数时,返回类型与函数定义之间空一格,当多个关联函数时,返回最长的类型与函数定义之间空一格,其他关联函数首字符与其对齐。 对内声明的参数除放在源文件中外,其他与对外声明的函数保持一致。

  • 示例

    命名顺序、空格、空行、参数换行请参考下图。

    int xxx_xxx_func1(void);
    int xxx_xxx_func2(void);
    
    int   xxx_xxx_func3(...);
    int   xxx_xxx_func4(...);
    void  xxx_xxx_func5(...);
    void *xxx_xxx_func6(...);
    

函数定义

  • 原则

    函数的定义原则上在源文件中实现,且其定义的顺序与声明保持一致; 在函数定义时,需要将大括号另起一行,且代码逻辑紧邻大括号后一行; 函数之前留一空行。

  • 示例

    int xxx_func1(void)
    {
        return 0;
    }
    
    int xxx_func2(void)
    {
        return 0
    }
    
    int xxx_func3(void)
    {
        return 0;
    }
    

函数调用

  • 原则

    函数调用风格与定义基本相同,在超过`80列`时按照相同的规范处理; 需要对函数调用的返回值进行判断,且判断语句与上一条语句之间不留空行。

  • 示例

    int xxx_func(void)
    {
        int ret;
    
        ret = xxx_func1(...);
        if (ret != 0) {
            ...
            return ret;
        }
    
        return 0;
    }
    

条件语句

  • 原则

    条件判断必须明确,如“if (var)”形式应该写为“if (var != 0)”; 指针是否为空时必须与NULL进行对比(不建议用0),整型返回值可通过与1/0进行对比。

  • 示例

    if (var != 0) {
        return -1;
    }
    
    if (ret != 0) {
        ...
        return -2;
    }
    

循环语句

  • 原则

    风格与条件判断语句类似。

  • 示例

    for (i = 0; i < 100; i++) {
        ...
    }
    
    while (ret == 1) {
        ...
    }
    
    do {
        ...
    } while(ret != 1);