范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

分享GitHub上一位老外的嵌入式C编码规范(收藏细读)

  最重要的一条规则
  编写代码时最重要的一条规则是:检查周围的代码并尝试模仿它。
  作为维护人员,如果收到的补丁明显与周围代码的编码风格不同,这是令人沮丧的。这是不尊重人的,就像某人穿着泥泞的鞋子走进一间一尘不染的房子。
  因此,无论本文推荐的是什么,如果已经编写了代码并且您正在对其进行修补,请保持其当前的样式一致,即使它不是您最喜欢的样式。 一般规则
  这里列出了最明显和最重要的一般规则。在你继续阅读其他章节之前,请仔细检查它们。 使用C99标准 不使用制表符,而是使用空格 每个缩进级别使用4个空格 在关键字和左括号之间使用一个空格 在函数名和左括号之间不要使用空格 int32_t a = sum(4, 3);              /* OK */ int32_t a = sum (4, 3);             /* Wrong */不要在变量/函数/宏/类型中使用 _或 前缀。这是为C语言本身保留的 对于严格的模块私有函数,使用prv_ name前缀 对于包含下划线_ char的变量/函数/宏/类型,只能使用小写字母 左花括号总是与关键字(for, while, do, switch, if,…)在同一行 size_t i; for (i = 0; i < 5; ++i) {           /* OK */ } for (i = 0; i < 5; ++i){            /* Wrong */ } for (i = 0; i < 5; ++i)             /* Wrong */ { }在比较操作符和赋值操作符之前和之后使用单个空格 // 微信公众号:嵌入式大杂烩 int32_t a; a = 3 + 4;              /* OK */ for (a = 0; a < 5; ++a) /* OK */ a=3+4;                  /* Wrong */ a = 3+4;                /* Wrong */ for (a=0;a<5;++a)       /* Wrong */每个逗号后用单空格 // 微信公众号:嵌入式大杂烩 func_name(5, 4);        /* OK */ func_name(4,3);         /* Wrong */不要初始化静态和全局变量为0(或NULL),让编译器为您做 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 */ }在同一行中声明所有相同类型的局部变量 void my_func(void) {     char a;             /* OK */     char b;             /* Wrong, variable with char type already exists */     char a, b;          /* OK */ }按顺序声明局部变量
  i.自定义结构和枚举
  ii.整数类型,更宽的无符号类型优先
  iii.单/双浮点 int my_func(void) {     /* 1 */     my_struct_t my;     /* First custom structures */     my_struct_ptr_t* p; /* Pointers too */      /* 2 */     uint32_t a;     int32_t b;     uint16_t c;     int16_t g;     char h;     /* ... */      /* 3 */     double d;     float f; }总是在块的开头声明局部变量,在第一个可执行语句之前 在for循环中声明计数器变量 /* 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) ...避免在声明中使用函数调用来赋值变量,除了单个变量 void a(void) {     /* Avoid function calls when declaring variable */     int32_t a, b = sum(1, 2);      /* Use this */     int32_t a, b;     b = sum(1, 2);      /* This is ok */     uint8_t a = 3, b = 4; }除了char、float或double之外,始终使用stdint.h标准库中声明的类型。例如,8位的uint8_t等 不要使用stdbool.h库。分别使用1或0表示真或假 /* OK */ uint8_t status; status = 0;  /* Wrong */ #include  bool status = true;永远不要与真实相比较。例如,使用 if(check_func()){…} 替换if (check_func() == 1) 总是将指针与空值进行比较 void* ptr;  /* ... */  /* OK, compare against NULL */ if (ptr == NULL || ptr != NULL) {  }  /* Wrong */ if (ptr || !ptr) {  }总是使用前增量(和递减),而不是后增量(和递减) int32_t a = 0; ...  a++;            /* Wrong */ ++a;            /* OK */  for (size_t j = 0; j < 10; ++j) {}  /* OK */总是使用size_t作为长度或大小变量 如果函数不应该修改指针所指向的内存,则总是使用const作为指针 如果不应该修改函数的形参或变量,则总是使用const /* When d could be modified, data pointed to by d could not be modified */ void my_func(const void* d) {  }  /* When d and data pointed to by d both could not be modified */ void my_func(const void* const d) {  }  /* Not required, it is advised */ void my_func(const size_t len) {  }  /* When d should not be modified inside function, only data pointed to by d could be modified */ void my_func(void* const d) {  }当函数可以接受任何类型的指针时,总是使用void *,不要使用uint8_t *。函数在实现时必须注意正确的类型转换 /*  * 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 *` or `const 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 */ }总是使用括号和sizeof操作符 不要使用变长数组。使用动态内存分配代替标准C malloc和自由函数,或者如果库/项目提供了自定义内存分配,使用它的实现看看LwMEM,一个自定义内存管理库。 /* OK */ #include  void my_func(size_t size) {     int32_t* arr;     arr = malloc(sizeof(*arr) * n); /* OK, Allocate memory */     arr = malloc(sizeof *arr * n);  /* Wrong, brackets for sizeof operator are missing */     if (arr == NULL) {         /* FAIL, no memory */     }      free(arr);  /* Free memory after usage */ }  /* Wrong */ void my_func(size_t size) {     int32_t arr[size];  /* Wrong, do not use VLA */ }总是将variable与0进行比较,除非它被视为布尔类型 永远不要将布尔处理的变量与0或1进行比较。用NOT(!)代替 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 */对于注释,总是使用/* comment */,即使是单行注释 在头文件中总是包含带有extern关键字的c++检查 每个函数都必须包含doxygen-enabled注释,即使函数是静态的 使用英文名称/文本的函数,变量,注释 变量使用小写字母 如果变量包含多个名称,请使用下划线。force_redraw。不要使用forceRedraw 对于C标准库的包含文件,请始终使用<和>。例如,# include < stdlib.h > 对于自定义库,请始终使用""。例如,# include"my_library.h" 当转换为指针类型时,总是将星号与类型对齐,例如。uint8_t* t = (uint8_t*)var_width_diff_type 始终尊重项目或库中已经使用的代码风格 注释不允许以//开头的注释。总是使用/* comment */,即使是单行注释 //This is comment (wrong) /* This is comment (ok) */对于多行注释,每行使用空格+星号 /*  * This is multi-line comments,  * written in 2 lines (ok)  */  /**  * Wrong, use double-asterisk only for doxygen documentation  */  /* * Single line comment without space before asterisk (wrong) */  /*  * Single line comment in multi-line configuration (wrong)  */  /* Single line comment (ok) */注释时使用12个缩进(12 * 4个空格)偏移量。如果语句大于12个缩进,将注释4-空格对齐(下面的例子)到下一个可用缩进 void my_func(void) {     char a, b;      a = call_func_returning_char_a(a);          /* This is comment with 12*4 spaces indent from beginning of line */     b = call_func_returning_char_a_but_func_name_is_very_long(a);   /* This is comment, aligned to 4-spaces indent */ }函数每个可以从模块外部访问的函数都必须包含函数原型(或声明) 函数名必须小写,可以用下划线_分隔 /* OK */ void my_func(void); void myfunc(void);  /* Wrong */ void MYFunc(void); void myFunc();当函数返回指针时,将星号对齐到返回类型 /* OK */ const char* my_func(void); my_struct_t* my_func(int32_t a, int32_t b);  /* Wrong */ const char *my_func(void); my_struct_t * my_func(void);对齐所有的功能原型(使用相同/相似的功能)以提高可读性 /* 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);函数实现必须在单独的行中包含返回类型和可选的其他关键字 /* OK */ int32_t foo(void) {     return 0; }  /* OK */ static const char* get_string(void) {     return "Hello world!r "; }  /* Wrong */ int32_t foo(void) {     return 0; }变量使变量名全部小写,下划线_字符可选 /* OK */ int32_t a; int32_t my_var; int32_t myvar;  /* Wrong */ int32_t A; int32_t myVar; int32_t MYVar;按类型将局部变量分组在一起 void foo(void) {     int32_t a, b;   /* OK */     char a;     char b;         /* Wrong, char type already exists */ }不要在第一个可执行语句之后声明变量 void foo(void) {     int32_t a;     a = bar();     int32_t b;      /* Wrong, there is already executable statement */ }你可以在下一个缩进级别中声明新的变量 int32_t a, b; a = foo(); if (a) {     int32_t c, d;   /* OK, c and d are in if-statement scope */     c = foo();     int32_t e;      /* Wrong, there was already executable statement inside block */ }用星号声明指针变量与类型对齐 /* OK */ char* a;  /* Wrong */ char *a; char * a;当声明多个指针变量时,可以使用星号对变量名进行声明 /* OK */ char *p, *n;结构、枚举类型定义结构名或枚举名必须小写,单词之间有下划线_字符 结构或枚举可以包含typedef关键字 所有结构成员都必须小写 所有枚举成员必须是大写的 结构/枚举必须遵循doxygen文档语法
  在声明结构体时,它可以使用以下三种不同的选项之一:
  1、当结构体仅用名称声明时,它的名称后不能包含_t后缀。 struct struct_name {     char* a;     char b; };
  2、当只使用typedef声明结构时,它的名称后面必须包含_t后缀。 typedef struct {     char* a;     char b; } struct_name_t;
  3、当结构用name和typedef声明时,它不能包含 t作为基本名称,它必须在它的名称后面包含 t后缀作为typedef部分。 typedef struct struct_name {     char* a;     char b;     char c; } struct_name_t;
  错误声明的例子及其建议的纠正: /* 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;在声明时初始化结构时,使用C99初始化风格 /* OK */ a_t a = {     .a = 4,     .b = 5, };  /* Wrong */ a_t a = {1, 2};当为函数句柄引入new typedef时,使用_fn后缀 /* Function accepts 2 parameters and returns uint8_t */ /* Name of typedef has `_fn` suffix */ typedef uint8_t (*my_func_typedef_fn)(uint8_t p1, const char* p2);复合语句每个复合语句必须包括左花括号和右花括号,即使它只包含1个嵌套语句 每个复合语句必须包含单个缩进;嵌套语句时,每个嵌套包含1个缩进大小 /* OK */ if (c) {     do_a(); } else {     do_b(); }  /* Wrong */ if (c)     do_a(); else     do_b();  /* Wrong */ if (c) do_a(); else do_b();在if或if-else-if语句的情况下,else必须与第一条语句的右括号在同一行 /* OK */ if (a) {  } else if (b) {  } else {  }  /* Wrong */ if (a) {  } else {  }  /* Wrong */ if (a) {  } else {  }在do-while语句的情况下,while部分必须与do部分的右括号在同一行 /* OK */ do {     int32_t a;     a = do_a();     do_b(a); } while (check());  /* Wrong */ do { /* ... */ } while (check());  /* Wrong */ do { /* ... */ } while (check());每一个开括号都需要缩进 if (a) {     do_a(); } else {     do_b();     if (c) {         do_c();     } }不要做没有花括号的复合语句,即使是单个语句。下面的例子展示了一些不好的做法 if (a) do_b(); else do_c();  if (a) do_a(); else do_b();空while循环、do-while循环或for循环必须包含花括号 /* OK */ while (is_register_bit_set()) {}  /* Wrong */ while (is_register_bit_set()); while (is_register_bit_set()) { } while (is_register_bit_set()) { }如果while(或for、do-while等)为空(嵌入式编程中也可能是这种情况),请使用空的单行括号 /* 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 */尽量避免在循环块内递增变量,参见示例 /* Not recommended */ int32_t a = 0; while (a < 10) {     .     ..     ...     ++a; }  /* Better */ for (size_t a = 0; a < 10; ++a) {  }  /* Better, if inc may not happen in every cycle */ for (size_t a = 0; a < 10; ) {     if (...) {         ++a;     } }分支语句为每个case语句添加单个缩进 使用额外的单缩进break语句在每个case或default /* OK, every case has single indent */ /* OK, every break has additional indent */ switch (check()) {     case 0:         do_a();         break;     case 1:         do_b();         break;     default:         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; }总是包含default语句 /* OK */ switch (var) {     case 0:         do_job();         break;     default: break; }  /* Wrong, default is missing */ switch (var) {     case 0:         do_job();         break; }如果需要局部变量,则使用花括号并在里面放入break语句。将左花括号放在case语句的同一行 switch (a) {     /* OK */     case 0: {         int32_t a, b;         char c;         a = 5;         /* ... */         break;     }      /* Wrong */     case 1:     {         int32_t a;         break;     }      /* Wrong, break shall be inside */     case 2: {         int32_t a;     }     break; }宏和预处理指令总是使用宏而不是文字常量,特别是对于数字 所有的宏必须是全大写的,并带有下划线_字符(可选),除非它们被明确标记为function,将来可能会被常规函数语法替换 /* OK */ #define MY_MACRO(x)         ((x) * (x))  /* Wrong */ #define square(x)           ((x) * (x))总是用圆括号保护输入参数 /* OK */ #define MIN(x, y)           ((x) < (y) ? (x) : (y))  /* Wrong */ #define MIN(x, y)           x < y ? x : y总是用括号保护最终的宏计算 /* 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))当宏使用多个语句时,使用do-while(0)语句保护它 typedef struct {     int32_t px, py; } point_t; point_t p;                  /* Define new point */  /* Wrong implementation */  /* Define macro to set point */ #define SET_POINT(p, x, y)  (p)->px = (x); (p)->py = (y)    /* 2 statements. Last one should not implement semicolon */  SET_POINT(&p, 3, 4);        /* Set point to position 3, 4. This evaluates to... */ (&p)->px = (3); (&p)->py = (4); /* ... to this. In this example this is not a problem. */  /* Consider this ugly code, however it is valid by C standard (not recommended) */ if (a)                      /* If a is true */     if (b)                  /* If b is true */         SET_POINT(&p, 3, 4);/* Set point to x = 3, y = 4 */     else         SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */  /* Evaluates to code below. Do you see the problem? */ if (a)     if (b)         (&p)->px = (3); (&p)->py = (4);     else         (&p)->px = (5); (&p)->py = (6);  /* Or if we rewrite it a little */ if (a)     if (b)         (&p)->px = (3);         (&p)->py = (4);     else         (&p)->px = (5);         (&p)->py = (6);  /*  * Ask yourself a question: To which `if` statement `else` keyword belongs?  *  * Based on first part of code, answer is straight-forward. To inner `if` statement when we check `b` condition  * Actual answer: Compilation error as `else` belongs nowhere  */  /* Better and correct implementation of macro */ #define SET_POINT(p, x, y)  do { (p)->px = (x); (p)->py = (y); } while (0)    /* 2 statements. No semicolon after while loop */ /* Or even better */ #define SET_POINT(p, x, y)  do {       /* Backslash indicates statement continues in new line */     (p)->px = (x);                       (p)->py = (y);                   } while (0)                             /* 2 statements. No semicolon after while loop */  /* Now original code evaluates to */ if (a)     if (b)         do { (&p)->px = (3); (&p)->py = (4); } while (0);     else         do { (&p)->px = (5); (&p)->py = (6); } while (0);  /* Every part of `if` or `else` contains only `1` inner statement (do-while), hence this is valid evaluation */  /* To make code perfect, use brackets for every if-ifelse-else statements */ if (a) {                    /* If a is true */     if (b) {                /* If b is true */         SET_POINT(&p, 3, 4);/* Set point to x = 3, y = 4 */     } else {         SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */     } }不缩进子语句内#if语句 /* 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)     #if defined(ABC)         /* do when ABC defined */     #endif /* defined(ABC) */ #else /* defined(XYZ) */     /* Do when XYZ not defined */ #endif /* !defined(XYZ) */文档
  文档化的代码允许doxygen解析和通用的html/pdf/latex输出,因此正确地执行是非常重要的。 对变量、函数和结构/枚举使用doxygen支持的文档样式 经常使用作为doxygen,不要使用@ 始终使用5x4空格(5个制表符)作为文本行开始的偏移量 /**  * brief           Holds pointer to first entry in linked list  *                  Beginning of this text is 5 tabs (20 spaces) from beginning of line  */ static type_t* list;每个结构/枚举成员都必须包含文档 注释的开头使用12x4空格偏移量 /**  * brief           This is point struct  *  ote            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.                                                     Since comment is very big,                                                     you may go to next line */ } point_t;  /**  * brief           Point color enumeration  */ typedef enum {     COLOR_RED,                                  /*!< Red color. This comment has 12x4                                                     spaces offset from beginning of line */     COLOR_GREEN,                                /*!< Green color */     COLOR_BLUE,                                 /*!< Blue color */ } point_color_t;函数的文档必须在函数实现中编写(通常是源文件) 函数必须包括简要和所有参数文档 如果每个参数分别为in或out输入和输出,则必须注意 如果函数返回某个值,则必须包含返回形参。这不适用于void函数 函数可以包含其他doxygen关键字,如note或warning 在参数名和描述之间使用冒号: /**  * 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  *  ote            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关键字指定哪个成员 /**  * 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) /**  * brief           Get data from input array  * param[in]       in: Input data  * return          Pointer to output data on success, `NULL` otherwise  */ const void * get_data(const void* in) {     return in; }宏的文档必须包括hideinitializer doxygen命令 /**  * brief           Get minimal value between `x` and `y`  * param[in]       x: First value  * param[in]       y: Second value  * return          Minimal value between `x` and `y`  * hideinitializer  */ #define MIN(x, y)       ((x) < (y) ? (x) : (y))头/源文件在文件末尾留下一个空行 每个文件都必须包括文件的doxygen注释和后跟空行的简要描述(使用doxygen时) /**  * file            template.h  * brief           Template include file  */                     /* Here is empty line */每个文件(头文件或源文件)必须包含许可证(开始注释包括单个星号,因为doxygen必须忽略这个) 使用与项目/库已经使用的相同的许可证 /**  * 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   */头文件必须包含保护符#ifndef 头文件必须包含c++检查 在c++检查之外包含外部头文件 首先用STL C文件包含外部头文件,然后是应用程序自定义文件 头文件必须包含其他所有头文件,以便正确编译,但不能包含更多头文件(如果需要,.c应该包含其余的头文件) 头文件必须只公开模块公共变量/类型/函数 在头文件中使用extern作为全局模块变量,稍后在源文件中定义它们 /* 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 */不要把.c文件包含在另一个.c文件中 .c文件应该首先包含相应的.h文件,然后是其他文件,除非另有明确的必要 在头文件中不包含模块私有声明 头文件示例(示例中没有license) /* License comes here */ #ifndef TEMPLATE_HDR_H #define TEMPLATE_HDR_H  /* Include headers */  #ifdef __cplusplus extern "C" { #endif /* __cplusplus */  /* File content here */  #ifdef __cplusplus } #endif /* __cplusplus */  #endif /* TEMPLATE_HDR_H */猜你喜欢:
  分享一种简单、实用的测量程序运行时间的方法
  简单认识认识mqtt及mosquitto
  STM32的ISP是怎么一回事?
  手把手教你在STM32上使用nanopb
  1024G 嵌入式资源大放送!包括但不限于C/C++、单片机、Linux等。私信回复 1024 ,即可免费获取!

比亚迪汉EV获年度最佳新能源车,竟向红旗提供刀片电池在1月20日举行的中央广播电视总台主办的2020中国汽车风云盛典中,比亚迪汉EV在众多竞争对手中脱颖而出,荣获年度最佳新能源车奖项,也成为唯一一款获奖的新能源车型。比亚迪汉是著名新电池技术实现突破,国产石墨烯电池即将量产,新能源汽车腾飞在即国产新能源汽车电池技术实现突破,石墨烯基电池即将量产!近日,广汽埃安官方发布全新动力电池科技预告,石墨烯基超级快充电池可在8分钟充满80,让充电像加油一样快速,同时硅负极电池还让电英唐智控收购含光刻机的日本公司获批,华为迎来转机?有戏8月25日,英唐智控在线上回应投资者提问时表示,其收购先锋微技术公司的事项已经获得日本政府的批准,而先锋微技术的主要业务包括提供MBE以及晶圆代工服务,自有的生产设备中包含有日本制丰田中国工厂再次部分停产,缺芯将致汽车行业损失超千亿美元近日有报道显示,丰田汽车再次暂停了其广州一条汽车产线。丰田中国方面已经对该消息进行了确认,表示产能调整仍然源于芯片相关零部件供应不足。不得不提的是,今年1月份,广汽本田就曾经因为芯因假公济私被特斯拉股东索赔168亿,马斯克刷新高管赔偿金额记录因涉嫌将股东利益装进了自己的口袋,埃隆马斯克被特斯拉股东告上法庭,索赔26亿美金(约合168亿人民币)。该案件将于7月12日开庭,马斯克将亲自出庭。2016年6月21日,特斯拉宣布FF美股挂牌上市,12个月内量产交车,贾跃亭必须回国美国当地时间7月22日,法拉第未来(FaradayFuture,下文简称FF)在美国正式挂牌上市。FFCEO毕福康在现场表示,这次合并交易能够保证FF91未来主义者版在12月内按时马斯克请求印度减税以支持特斯拉建厂,印本土车企集体说No印度对进口汽车的高额征税成为了阻挡特斯拉的障碍。近日有媒体报道称,特斯拉再次呼吁印度政府调低汽车税率的请求,依然遭到了拒绝,印度政府表示暂时不考虑削减电动汽车进口关税。报道显示,印特斯拉FSD收益将超过卖车,媒体却认为用户只是在充值做测试最近,特斯拉更新了完全自动驾驶(FSD)服务的Beta9版本,该版本采用了TeslaVision光学视觉系统,仅依赖光学图像,而无需任何雷达传感器就能实现自动驾驶导航自动变道自动泊中兴走出阴影小露锋芒,将首发屏下摄像头技术,中兴再度起航众所周知,智能手机的一个重要发展方向就是真全面屏,最理想的解决方案莫过于屏下摄像头技术,这也成为众多手机厂商竞争的焦点,出乎我们的意料以外,即将首发屏下摄像头技术的手机品牌并不是华小米被纳入黑名单,谷歌多项服务瘫痪,掌握核心科技已经迫在眉睫2020年,科技圈最大的新闻莫过于华为遭到美国政府的全方位制裁,美国制裁华为不仅仅是因为华为公司在5g领域一骑独尘,智能手机业务超越苹果,更是整个国家战略的需要,其实,打压华为并不云计算巨头微软加注汽车业,联合Arrival加速向数字化平台转型国外电动汽车制造厂商Arrival宣布与微软达成了合作,将使用微软公用云服务端平台Azure为汽车行业开发其数字车队和车辆功能。双方将一同开发汽车开放数据平台。针对此次合作,微软汽
JEETONE升级版真无线蓝牙耳机,粉嫩有佳心有所属不知道大家有没有发现,现在的数码产品,例如新款手机,屏幕是越来越大,份量也是越来越足,是不是货真价实就要分量重呢?并非如此,数码周边产品很多,蓝牙耳机也是必备之佳品,而蓝牙耳机通过ORICO扩展坞,苹果Macbook专用,usb3。0速度能跑满作为果粉来讲,一直对于苹果的产品情有独钟,但是在日常生活中遇到的一些事情,也是相爱相恨情感交织,凌乱自己的秀发。譬如自己现有的MacbookAir电脑,买来之后,一方面对IOS的苹解放双手的好帮手,拓牛T1智能垃圾桶初体验单身贵族为什么这么讲呢,因为单身狗也可以拥有自己的作息时间和生活习惯,同时也拥有着自己的爱好和拿手绝活。但是对于懒字头上一把刀,有时竟然哑口无言,智能默默地低头沉思了。熬夜喝完的啤vivoNEX5工程图曝光,后置四摄配4nm芯片,还有微云台防抖技术在国产手机中,vivo手机是一家拥有强大RD和创新实力的手机厂商。通过近年来的不断探索和努力,为行业推出了许多创新和受欢迎的产品。这些产品不仅为RD和vivo手机的设计提供了宝贵的罗永浩直播带货行李箱,地平线8号与你一起旅行说起行李箱,就不免想起了第一次做飞机的时候的情形。对于出远门是新媳妇上花轿第一回,当时出差一周,因为南北差异和温度差都很大,自己带的东西并不能满足现实的需要。因此从当地购买了时令衣体美身健魅力无限,华为智选mobifitness动感单车对标私人教练生命在于运动,每天锻炼一小时,健康工作几十年,幸福快乐一辈子。不论你是996还是朝九晚五,不论你是王权富贵还是平头老百姓,健康对谁来讲都是非常公平的一件事情,因此对于生命的敬畏和健iPhone13和iPhone12参数对比,你觉得哪个更值得入手众所周知,苹果手机是一家知名且备受喜爱的手机厂商,也是一家为智能手机发展做出突出贡献的手机厂商。在智能手机的发展中,苹果手机给行业带来了很多经典又受欢迎的产品,比如iPhone4i我国最能喝酒的省份,排名第一当属山东,梅醉过在我国最能喝酒的省份,据不完全统计,前六名分别是东北三省江苏省四川省河南省内蒙古山东省。为什么要说这些呢,因为今天要跟大家分享喝酒不上头,复兴2900年东方梅酒本味,更清爽,更轻松荣耀60概念机后置五摄配天玑2000,还有超级True加持为了使得消费者的需求得到满足,手机厂商快马加鞭的研发新的产品,于是在手机厂商的不懈努力下,智能手机迎来了飞速发展。HONOR是一家RD实力毋庸置疑的手机厂商,也是一家创新力十足的手三星S22最新消息,参数配置进一步曝光,三星这次很豪横三星手机是全球知名的手机厂商,是研发创新的领导者。所以,近年来,三星手机为行业带来了许多令人难忘的产品,像三星S7三星S10三星S21等。这些产品不仅拥有引领潮流的设计,还取得了丰腾讯看点的高考答卷十年树木,百年树人。在中国传统语境中,教育从来是一项需要细细打磨的事业。但一场突如其来的疫情为全社会按下了暂停键,使得万千学子与家长陷入焦虑之中。这其中,即将迎来人生第一个重大转折