C语言

最重要的一条规则

编写代码时最重要的一条规则是:检查周围的代码并尝试模仿它。
作为维护人员,如果收到的补丁明显与周围代码的编码风格不同,这是令人沮丧的。这是不尊重人的,就像某人穿着泥泞的鞋子走进一间一尘不染的房子。
因此,无论本文推荐的是什么,如果已经编写了代码并且您正在对其进行修补,请保持其当前的样式一致,即使它不是您最喜欢的样式。

一般性的规则

这里列出了最明显和最重要的一般规则。在继续阅读其他章节之前,请仔细检查它们:

  • 使用C99标准
  • 不使用制表符,而是使用空格
  • 每个缩进级别使用4个空格
  • 在关键字和左括号之间使用一个空格
  • 在函数名和左括号之间不要使用空格

    1. int32_t a = sum(4, 3); /* OK */
    2. int32_t a = sum (4, 3); /* Wrong */
  • 不要在变量/函数/宏/类型中使用_或前缀。这是为C语言本身保留的

  • 对于严格的模块私有函数,使用prv_ name前缀
  • 对于包含下划线_ char的变量/函数/宏/类型,只能使用小写字母
  • 左花括号总是与关键字(for, while, do, switch, if,…)在同一行

    1. size_t i;
    2. for (i = 0; i < 5; ++i) { /* OK */
    3. }
    4. for (i = 0; i < 5; ++i){ /* Wrong */
    5. }
    6. for (i = 0; i < 5; ++i) /* Wrong */
    7. {
    8. }
  • 在比较操作符和赋值操作符之前和之后使用单个空格

    1. int32_t a;
    2. a = 3 + 4; /* OK */
    3. for (a = 0; a < 5; ++a) /* OK */
    4. a=3+4; /* Wrong */
    5. a = 3+4; /* Wrong */
    6. for (a=0;a<5;++a) /* Wrong */
  • 每个逗号后用单空格

    1. func_name(5, 4); /* OK */
    2. func_name(4,3); /* Wrong */
  • 不要初始化静态和全局变量为0(或NULL),让编译器为您做 ```c static int32_t a; / OK / static int32_t b = 4; / OK / static int32_t a = 0; / Wrong /

void my_func(void) { static int32_t ptr;/ OK / static char abc = 0;/ Wrong */ }

  1. - 在同一行中声明所有相同类型的局部变量
  2. ```c
  3. void my_func(void) {
  4. char a; /* OK */
  5. char b; /* Wrong, variable with char type already exists */
  6. char a, b; /* OK */
  7. }
  • 按顺序声明局部变量

i. 自定义结构和枚举
ii. 整数类型,更宽的无符号类型优先
iii. 单/双浮点

  1. int my_func(void) {
  2. /* 1 */
  3. my_struct_t my; /* First custom structures */
  4. my_struct_ptr_t* p; /* Pointers too */
  5. /* 2 */
  6. uint32_t a;
  7. int32_t b;
  8. uint16_t c;
  9. int16_t g;
  10. char h;
  11. /* ... */
  12. /* 3 */
  13. double d;
  14. float f;
  15. }
  • 总是在块的开头声明局部变量,在第一个可执行语句之前
  • 在for循环中声明计数器变量 ```c / OK / for (size_t i = 0; i < 10; ++i)

/ OK, if you need counter variable later / size_t i; for (i = 0; i < 10; ++i) { if (…) { break; } } if (i * 10) {

}

/ Wrong / size_t i; for (i = 0; i < 10; ++i) …

  1. - 避免在声明中使用函数调用来赋值变量,除了单个变量
  2. ```c
  3. void a(void) {
  4. /* Avoid function calls when declaring variable */
  5. int32_t a, b = sum(1, 2);
  6. /* Use this */
  7. int32_t a, b;
  8. b = sum(1, 2);
  9. /* This is ok */
  10. uint8_t a = 3, b = 4;
  11. }
  • 除了charfloatdouble之外,始终使用stdint.h标准库中声明的类型。例如,8位的uint8_t等
  • 不要使用stdbool.h库。分别使用1或0表示真或假 ```c / OK / uint8_t status; status = 0;

/ Wrong /

include

bool status = true;

  1. - 永远不要与真实相比较。例如,使用`if(check_func()){…}`替换`if (check_func() * 1)`
  2. - 总是将指针与空值进行比较
  3. ```c
  4. void* ptr;
  5. /* ... */
  6. /* OK, compare against NULL */
  7. if (ptr * NULL || ptr != NULL) {
  8. }
  9. /* Wrong */
  10. if (ptr || !ptr) {
  11. }
  • 总是使用前增量(和递减),而不是后增量(和递减) ```c int32_t a = 0; …

a++; / Wrong / ++a; / OK /

for (size_t j = 0; j < 10; ++j) {} / OK /

  1. - 总是使用`size_t`作为长度或大小变量
  2. - 如果函数不应该修改指针所指向的内存,则总是使用`const`作为指针
  3. - 如果不应该修改函数的形参或变量,则总是使用`const`
  4. ```c
  5. /* When d could be modified, data pointed to by d could not be modified */
  6. void
  7. my_func(const void* d) {
  8. }
  9. /* When d and data pointed to by d both could not be modified */
  10. void
  11. my_func(const void* const d) {
  12. }
  13. /* Not required, it is advised */
  14. void
  15. my_func(const size_t len) {
  16. }
  17. /* When d should not be modified inside function, only data pointed to by d could be modified */
  18. void
  19. my_func(void* const d) {
  20. }
  • 当函数可以接受任何类型的指针时,总是使用void *,不要使用uint8_t *。函数在实现时必须注意正确的类型转换 ```c /*
    • To send data, function should not modify memory pointed to by data variable
    • thus const keyword is important *
    • To send generic data (or to write them to file)
    • any type may be passed for data,
    • thus use void * / / OK example / void send_data(const void data, size_t len) { / OK / / Do not cast `void orconst void ` / const uint8_t d = data;/ Function handles proper type for internal usage */ }

void send_data(const void data, int len) { / Wrong, not not use int */ }

  1. - 总是使用括号和`sizeof`操作符
  2. - 不要使用变长数组。使用动态内存分配代替标准C malloc和自由函数,或者如果库/项目提供了自定义内存分配,使用它的实现看看`LwMEM`,一个自定义内存管理库。
  3. ```c
  4. /* OK */
  5. #include <stdlib.h>
  6. void
  7. my_func(size_t size) {
  8. int32_t* arr;
  9. arr = malloc(sizeof(*arr) * n); /* OK, Allocate memory */
  10. arr = malloc(sizeof *arr * n); /* Wrong, brackets for sizeof operator are missing */
  11. if (arr * NULL) {
  12. /* FAIL, no memory */
  13. }
  14. free(arr); /* Free memory after usage */
  15. }
  16. /* Wrong */
  17. void
  18. my_func(size_t size) {
  19. int32_t arr[size]; /* Wrong, do not use VLA */
  20. }
  • 总是将variable与0进行比较,除非它被视为布尔类型
  • 永远不要将布尔处理的变量与0或1进行比较。用NOT(!)代替 ```c size_t length = 5; / Counter variable / uint8_t is_ok = 0; / Boolean-treated variable / if (length) / Wrong, length is not treated as boolean / if (length > 0) / OK, length is treated as counter variable containing multi values, not only 0 or 1 / if (length 0) / OK, length is treated as counter variable containing multi values, not only 0 or 1 */

if (is_ok) / OK, variable is treated as boolean / if (!is_ok) / OK, -||- / if (is_ok 1) / Wrong, never compare boolean variable against 1! / if (is_ok 0) / Wrong, use ! for negative check /

  1. - 对于注释,总是使用`/* comment */`,即使是单行注释
  2. - 在头文件中总是包含带有`extern`关键字的c++检查
  3. - 每个函数都必须包含doxygen-enabled注释,即使函数是静态的
  4. - 使用英文名称`/`文本的函数,变量,注释
  5. - 变量使用小写字母
  6. - 如果变量包含多个名称,请使用下划线。`force_redraw`。不要使用`forceRedraw`
  7. - 对于C标准库的包含文件,请始终使用<和>。例如,`# include < stdlib.h >`
  8. - 对于自定义库,请始终使用`""`。例如,`# include "my_library.h"`
  9. - 当转换为指针类型时,总是将星号与类型对齐,例如。`uint8_t* t = (uint8_t*)var_width_diff_type`
  10. - 始终尊重项目或库中已经使用的代码风格
  11. <a name="RZIT6"></a>
  12. ### 注释相关的规则
  13. - 不允许以//开头的注释。总是使用② `comment */`,即使是单行注释
  14. - 对于多行注释,每行使用空格+星号
  15. ```c
  16. /*
  17. * This is multi-line comments,
  18. * written in 2 lines (ok)
  19. */
  20. /**
  21. * Wrong, use double-asterisk only for doxygen documentation
  22. */
  23. /*
  24. * Single line comment without space before asterisk (wrong)
  25. */
  26. /*
  27. * Single line comment in multi-line configuration (wrong)
  28. */
  29. /* Single line comment (ok) */
  • 注释时使用12个缩进(12 * 4个空格)偏移量。如果语句大于12个缩进,将注释4-空格对齐(下面的例子)到下一个可用缩进

    1. void my_func(void) {
    2. char a, b;
    3. a = call_func_returning_char_a(a); /* This is comment with 12*4 spaces indent from beginning of line */
    4. b = call_func_returning_char_a_but_func_name_is_very_long(a); /* This is comment, aligned to 4-spaces indent */
    5. }

    函数定义的规则

  • 每个可以从模块外部访问的函数都必须包含函数原型(或声明)

  • 函数名必须小写,可以用下划线_分隔。(这个原则好像因人而异) ```c / OK / void my_func(void); void myfunc(void);

/ Wrong / void MYFunc(void); void myFunc();

  1. - 当函数返回指针时,将星号对齐到返回类型
  2. ```c
  3. /* OK */
  4. const char* my_func(void);
  5. my_struct_t* my_func(int32_t a, int32_t b);
  6. /* Wrong */
  7. const char *my_func(void);
  8. my_struct_t * my_func(void);
  • 对齐所有的功能原型(使用相同/相似的功能)以提高可读性 ```c / OK, function names aligned / void set(int32_t a); my_type_t get(void); my_ptr_t* get_ptr(void);

/ Wrong / void set(int32_t a); const char * get(void);

  1. - 函数实现必须在单独的行中包含返回类型和可选的其他关键字
  2. ```c
  3. /* OK */
  4. int32_t
  5. foo(void) {
  6. return 0;
  7. }
  8. /* OK */
  9. static const char*
  10. get_string(void) {
  11. return "Hello world!\r\n";
  12. }
  13. /* Wrong */
  14. int32_t foo(void) {
  15. return 0;
  16. }

变量相关的规则

  • 使变量名全部小写,下划线_字符可选 ```c / OK / int32_t a; int32_t my_var; int32_t myvar;

/ Wrong / int32_t A; int32_t myVar; int32_t MYVar;

  1. - 按类型将局部变量分组在一起
  2. ```c
  3. void foo(void) {
  4. int32_t a, b; /* OK */
  5. char a;
  6. char b; /* Wrong, char type already exists */
  7. }
  • 不要在第一个可执行语句之后声明变量

    1. void foo(void) {
    2. int32_t a;
    3. a = bar();
    4. int32_t b; /* Wrong, there is already executable statement */
    5. }
  • 可以在下一个缩进级别中声明新的变量

    1. int32_t a, b;
    2. a = foo();
    3. if (a) {
    4. int32_t c, d; /* OK, c and d are in if-statement scope */
    5. c = foo();
    6. int32_t e; /* Wrong, there was already executable statement inside block */
    7. }
  • 用星号声明指针变量与类型对齐 ```c / OK / char* a;

/ Wrong / char a; char a;

  1. - 当声明多个指针变量时,可以使用星号对变量名进行声明
  2. ```c
  3. /* OK */
  4. char *p, *n;

结构、枚举类型定义

  • 结构名或枚举名必须小写,单词之间有下划线_字符
  • 结构或枚举可以包含typedef关键字
  • 所有结构成员都必须小写
  • 所有枚举成员必须是大写的
  • 结构/枚举必须遵循doxygen文档语法
  • 在声明结构体时,它可以使用以下三种不同的选项之一:

    1. 当结构体仅用名称声明时,它的名称后不能包含_t后缀。

    1. struct struct_name {
    2. char* a;
    3. char b;
    4. };

    2. 当只使用typedef声明结构时,它的名称后面必须包含_t后缀。

    1. typedef struct {
    2. char* a;
    3. char b;
    4. } struct_name_t;

    3. 当结构用nametypedef声明时,它不能包含t作为基本名称,它必须在它的名称后面包含t后缀作为typedef部分。

    1. typedef struct struct_name {
    2. char* a;
    3. char b;
    4. char c;
    5. } struct_name_t;
  • 错误声明的例子及其建议的纠正: ```c / a and b must be separated to 2 lines / / Name of structure with typedef must include _t suffix / typedef struct { int32_t a, b; } a;

/ Corrected version / typedef struct { int32_t a; int32_t b; } a_t;

/ Wrong name, it must not include _t suffix / struct name_t { int32_t a; int32_t b; };

/ Wrong parameters, must be all uppercase / typedef enum { MY_ENUM_TESTA, my_enum_testb, } my_enum_t;

  1. - 在声明时初始化结构时,使用C99初始化风格
  2. ```c
  3. /* OK */
  4. a_t a = {
  5. .a = 4,
  6. .b = 5,
  7. };
  8. /* Wrong */
  9. a_t a = {1, 2};
  • 当为函数句柄引入new typedef时,使用_fn后缀

    1. /* Function accepts 2 parameters and returns uint8_t */
    2. /* Name of typedef has `_fn` suffix */
    3. typedef uint8_t (*my_func_typedef_fn)(uint8_t p1, const char* p2);

    复合语句规则

  • 每个复合语句必须包括左花括号和右花括号,即使它只包含1个嵌套语句

  • 每个复合语句必须包含单个缩进;嵌套语句时,每个嵌套包含1个缩进大小 ```c / OK / if (c) { do_a(); } else { do_b(); }

/ Wrong / if (c) do_a(); else do_b();

/ Wrong / if (c) do_a(); else do_b();

  1. - ifif-else-if语句的情况下,else必须与第一条语句的右括号在同一行
  2. ```c
  3. /* OK */
  4. if (a) {
  5. } else if (b) {
  6. } else {
  7. }
  8. /* Wrong */
  9. if (a) {
  10. }
  11. else {
  12. }
  13. /* Wrong */
  14. if (a) {
  15. }
  16. else
  17. {
  18. }
  • 在do-while语句的情况下,while部分必须与do部分的右括号在同一行 ```c / OK / do { int32_t a; a = do_a(); do_b(a); } while (check());

/ Wrong / do { // } while (check());

/ Wrong / do { // } while (check());

  1. - 每一个开括号都需要缩进
  2. ```c
  3. if (a) {
  4. do_a();
  5. } else {
  6. do_b();
  7. if (c) {
  8. do_c();
  9. }
  10. }
  • 不要做没有花括号的复合语句,即使是单个语句。下面的例子展示了一些不好的做法 ```c if (a) do_b(); else do_c();

if (a) do_a(); else do_b();

  1. - while循环、do-while循环或for循环必须包含花括号
  2. ```c
  3. /* OK */
  4. while (is_register_bit_set()) {}
  5. /* Wrong */
  6. while (is_register_bit_set());
  7. while (is_register_bit_set()) { }
  8. while (is_register_bit_set()) {
  9. }
  • 如果while(或for、do-while等)为空(嵌入式编程中也可能是这种情况),请使用空的单行括号 ```c / Wait for bit to be set in embedded hardware unit uint32_t addr = HW_PERIPH_REGISTER_ADDR;

/ Wait bit 13 to be ready / while (addr & (1 << 13)) {} / OK, empty loop contains no spaces inside curly brackets / while (addr & (1 << 13)) { } / Wrong / while (addr & (1 << 13)) { / Wrong */

} while (addr & (1 << 13)); / Wrong, curly brackets are missing. Can lead to compiler warnings or unintentional bugs */

  1. - 尽量避免在循环块内递增变量,参见示例
  2. ```c
  3. /* Not recommended */
  4. int32_t a = 0;
  5. while (a < 10) {
  6. .
  7. ..
  8. ...
  9. ++a;
  10. }
  11. /* Better */
  12. for (size_t a = 0; a < 10; ++a) {
  13. }
  14. /* Better, if inc may not happen in every cycle */
  15. for (size_t a = 0; a < 10; ) {
  16. if (...) {
  17. ++a;
  18. }
  19. }

分支语句规则

  • 为每个case语句添加单个缩进
  • 使用额外的单缩进break语句在每个casedefault ```c / OK, every case has single indent / / OK, every break has additional indent / switch (check()) { case 0:
    1. do_a();
    2. break;
    case 1:
    1. do_b();
    2. break;
    default:
    1. break;
    }

/ Wrong, case indent missing / switch (check()) { case 0: do_a(); break; case 1: do_b(); break; default: break; }

/ Wrong / switch (check()) { case 0: do_a(); break; / Wrong, break must have indent as it is under case / case 1: do_b(); / Wrong, indent under case is missing / break; default: break; }

  1. - 总是包含`default`语句
  2. ```c
  3. /* OK */
  4. switch (var) {
  5. case 0:
  6. do_job();
  7. break;
  8. default: break;
  9. }
  10. /* Wrong, default is missing */
  11. switch (var) {
  12. case 0:
  13. do_job();
  14. break;
  15. }
  • 如果需要局部变量,则使用花括号并在里面放入break语句。将左花括号放在case语句的同一行

    1. switch (a) {
    2. /* OK */
    3. case 0: {
    4. int32_t a, b;
    5. char c;
    6. a = 5;
    7. /* ... */
    8. break;
    9. }
    10. /* Wrong */
    11. case 1:
    12. {
    13. int32_t a;
    14. break;
    15. }
    16. /* Wrong, break shall be inside */
    17. case 2: {
    18. int32_t a;
    19. }
    20. break;
    21. }

    宏和预处理指令

  • 总是使用宏而不是文字常量,特别是对于数字

  • 所有的宏必须是全大写的,并带有下划线_字符(可选),除非它们被明确标记为function,将来可能会被常规函数语法替换 ```c / OK /

    define MY_MACRO(x) ((x) * (x))

/ Wrong /

define square(x) ((x) * (x))

  1. - 总是用圆括号保护输入参数
  2. ```c
  3. /* OK */
  4. #define MIN(x, y) ((x) < (y) ? (x) : (y))
  5. /* Wrong */
  6. #define MIN(x, y) x < y ? x : y
  • 总是用括号保护最终的宏计算 ```c / Wrong /

    define MIN(x, y) (x) < (y) ? (x) : (y)

    define SUM(x, y) (x) + (y)

/ Imagine result of this equation using wrong SUM implementation / int32_t x = 5 SUM(3, 4); / Expected result is 5 7 = 35 / int32_t x = 5 (3) + (4); / It is evaluated to this, final result = 19 which is not what we expect */

/ Correct implementation /

define MIN(x, y) ((x) < (y) ? (x) : (y))

define SUM(x, y) ((x) + (y))

  1. - 当宏使用多个语句时,使用`do-while(0)`语句保护它
  2. ```c
  3. typedef struct {
  4. int32_t px, py;
  5. } point_t;
  6. point_t p; /* Define new point */
  7. /* Wrong implementation */
  8. /* Define macro to set point */
  9. #define SET_POINT(p, x, y) (p)->px = (x); (p)->py = (y) /* 2 statements. Last one should not implement semicolon */
  10. SET_POINT(&p, 3, 4); /* Set point to position 3, 4. This evaluates to... */
  11. (&p)->px = (3); (&p)->py = (4); /* ... to this. In this example this is not a problem. */
  12. /* Consider this ugly code, however it is valid by C standard (not recommended) */
  13. if (a) /* If a is true */
  14. if (b) /* If b is true */
  15. SET_POINT(&p, 3, 4);/* Set point to x = 3, y = 4 */
  16. else
  17. SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */
  18. /* Evaluates to code below. Do you see the problem? */
  19. if (a)
  20. if (b)
  21. (&p)->px = (3); (&p)->py = (4);
  22. else
  23. (&p)->px = (5); (&p)->py = (6);
  24. /* Or if we rewrite it a little */
  25. if (a)
  26. if (b)
  27. (&p)->px = (3);
  28. (&p)->py = (4);
  29. else
  30. (&p)->px = (5);
  31. (&p)->py = (6);
  32. /*
  33. * Ask yourself a question: To which `if` statement `else` keyword belongs?
  34. *
  35. * Based on first part of code, answer is straight-forward. To inner `if` statement when we check `b` condition
  36. * Actual answer: Compilation error as `else` belongs nowhere
  37. */
  38. /* Better and correct implementation of macro */
  39. #define SET_POINT(p, x, y) do { (p)->px = (x); (p)->py = (y); } while (0) /* 2 statements. No semicolon after while loop */
  40. /* Or even better */
  41. #define SET_POINT(p, x, y) do { \ /* Backslash indicates statement continues in new line */
  42. (p)->px = (x); \
  43. (p)->py = (y); \
  44. } while (0) /* 2 statements. No semicolon after while loop */
  45. /* Now original code evaluates to */
  46. if (a)
  47. if (b)
  48. do { (&p)->px = (3); (&p)->py = (4); } while (0);
  49. else
  50. do { (&p)->px = (5); (&p)->py = (6); } while (0);
  51. /* Every part of `if` or `else` contains only `1` inner statement (do-while), hence this is valid evaluation */
  52. /* To make code perfect, use brackets for every if-ifelse-else statements */
  53. if (a) { /* If a is true */
  54. if (b) { /* If b is true */
  55. SET_POINT(&p, 3, 4);/* Set point to x = 3, y = 4 */
  56. } else {
  57. SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */
  58. }
  59. }
  • 不缩进子语句内#if语句 ```c / OK /

    if defined(XYZ)

    if defined(ABC)

    / do when ABC defined /

    endif / defined(ABC) /

    else / defined(XYZ) /

    / Do when XYZ not defined /

    endif / !defined(XYZ) /

/ Wrong /

if defined(XYZ)

  1. #if defined(ABC)
  2. /* do when ABC defined */
  3. #endif /* defined(ABC) */

else / defined(XYZ) /

  1. /* Do when XYZ not defined */

endif / !defined(XYZ) /

  1. <a name="SPAv8"></a>
  2. ### 文档
  3. - 文档化的代码允许`doxygen`解析和通用的html/pdf/latex输出,因此正确地执行是非常重要的。
  4. - 对变量、函数和结构`/`枚举使用`doxygen`支持的文档样式
  5. - 经常使用`\`作为`doxygen`,不要使用`@`
  6. - 始终使用5x4空格(5个制表符)作为文本行开始的偏移量
  7. ```c
  8. /**
  9. * \brief Holds pointer to first entry in linked list
  10. * Beginning of this text is 5 tabs (20 spaces) from beginning of line
  11. */
  12. static
  13. type_t* list;
  • 每个结构/枚举成员都必须包含文档
  • 注释的开头使用12x4空格偏移量 ```c /**
    • \brief This is point struct
    • \note This structure is used to calculate all point
    • related stuff / typedef struct { int32_t x; /!< Point X coordinate / int32_t y; /!< Point Y coordinate / int32_t size; /!< Point size.
      1. Since comment is very big,
      2. you may go to next line */
      } point_t;

/**

  • \brief Point color enumeration / typedef enum { COLOR_RED, /!< Red color. This comment has 12x4
    1. spaces offset from beginning of line */
    COLOR_GREEN, /!< Green color / COLOR_BLUE, /!< Blue color / } point_color_t; ```
  • 函数的文档必须在函数实现中编写(通常是源文件)
  • 函数必须包括简要和所有参数文档
  • 如果每个参数分别为in或out输入和输出,则必须注意
  • 如果函数返回某个值,则必须包含返回形参。这不适用于void函数
  • 函数可以包含其他doxygen关键字,如note或warning
  • 在参数名和描述之间使用冒号: ```c /**
    • \brief Sum 2 numbers
    • \param[in] a: First number
    • \param[in] b: Second number
    • \return Sum of input values */ int32_t sum(int32_t a, int32_t b) { return a + b; }

/**

  • \brief Sum 2 numbers and write it to pointer
  • \note This function does not return value, it stores it to pointer instead
  • \param[in] a: First number
  • \param[in] b: Second number
  • \param[out] result: Output variable used to save result / void void_sum(int32_t a, int32_t b, int32_t result) { *result = a + b; } ```
  • 如果函数返回枚举的成员,则使用ref关键字指定哪个成员 ```c /**
    • \brief My enumeration / typedef enum { MY_ERR, /!< Error value / MY_OK /!< OK value */ } my_enum_t;

/**

  • \brief Check some value
  • \return \ref MY_OK on success, member of \ref my_enum_t otherwise */ my_enum_t check_value(void) { return MY_OK; } ```
  • 对常量或数字使用符号(‘ NULL ‘ => NULL)

    1. /**
    2. * \brief Get data from input array
    3. * \param[in] in: Input data
    4. * \return Pointer to output data on success, `NULL` otherwise
    5. */
    6. const void *
    7. get_data(const void* in) {
    8. return in;
    9. }
  • 宏的文档必须包括hideinitializer doxygen命令

    1. /**
    2. * \brief Get minimal value between `x` and `y`
    3. * \param[in] x: First value
    4. * \param[in] y: Second value
    5. * \return Minimal value between `x` and `y`
    6. * \hideinitializer
    7. */
    8. #define MIN(x, y) ((x) < (y) ? (x) : (y))

    头/源文件

  • 在文件末尾留下一个空行

  • 每个文件都必须包括文件的doxygen注释和后跟空行的简要描述(使用doxygen时)

    1. /**
    2. * \file template.h
    3. * \brief Template include file
    4. */
    5. /* Here is empty line */
  • 每个文件(头文件或源文件)必须包含许可证(开始注释包括单个星号,因为doxygen必须忽略这个)

  • 使用与项目/库已经使用的相同的许可证 ```c /**
    • \file template.h
    • \brief Template include file */

/*

  • Copyright (c) year FirstName LASTNAME *
  • Permission is hereby granted, free of charge, to any person
  • obtaining a copy of this software and associated documentation
  • files (the “Software”), to deal in the Software without restriction,
  • including without limitation the rights to use, copy, modify, merge,
  • publish, distribute, sublicense, and/or sell copies of the Software,
  • and to permit persons to whom the Software is furnished to do so,
  • subject to the following conditions: *
  • The above copyright notice and this permission notice shall be
  • included in all copies or substantial portions of the Software. *
  • THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
  • EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  • OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
  • AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  • HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  • WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  • FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  • OTHER DEALINGS IN THE SOFTWARE. *
  • This file is part of library_name. *
  • Author: FirstName LASTNAME optional_email@example.com */ ```
  • 头文件必须包含保护符#ifndef
  • 头文件必须包含c++检查
  • 在c++检查之外包含外部头文件
  • 首先用STL C文件包含外部头文件,然后是应用程序自定义文件
  • 头文件必须包含其他所有头文件,以便正确编译,但不能包含更多头文件(如果需要,.c应该包含其余的头文件)
  • 头文件必须只公开模块公共变量/类型/函数
  • 在头文件中使用extern作为全局模块变量,稍后在源文件中定义它们 ```c / file.h … /

    ifndef …

extern int32_t my_variable; / This is global variable declaration in header /

endif

/ file.c … / int32_t my_variable; / Actually defined in source /

  1. - 不要把.c文件包含在另一个.c文件中
  2. - .c文件应该首先包含相应的.h文件,然后是其他文件,除非另有明确的必要
  3. - 在头文件中不包含模块私有声明
  4. - 头文件示例(示例中没有`license`)
  5. ```c
  6. /* License comes here */
  7. #ifndef TEMPLATE_HDR_H
  8. #define TEMPLATE_HDR_H
  9. /* Include headers */
  10. #ifdef __cplusplus
  11. extern "C" {
  12. #endif /* __cplusplus */
  13. /* File content here */
  14. #ifdef __cplusplus
  15. }
  16. #endif /* __cplusplus */
  17. #endif /* TEMPLATE_HDR_H */