当前位置: 首页 > news >正文

嵌入式C语言编程规范

嵌入式C语言编程规范

文章目录

  • 嵌入式C语言编程规范
    • 前言
    • 一、标识符命名
      • 1.1 通用规则
      • 1.2 文件命名
      • 1.3 宏和常量命名
      • 1.4 结构体和枚举命名
      • 1.5 变量命名
      • 1.6 函数命名
    • 二、文件
      • 2.1 源文件
      • 2.2 头文件
    • 三、变量
      • 3.1 数据类型
      • 3.2 全局和局部变量
    • 四、宏和常量
      • 4.1 宏定义
      • 4.2 常量
    • 五、函数
      • 5.1 函数体
      • 5.2 参数和返回值
    • 六、语句和表达式
      • 6.1 表达式
      • 6.2 语句
    • 七、注释
      • 7.1 要求
      • 7.2 格式
    • 八、排版与格式
      • 8.1 通用格式
      • 8.2 文件示例
    • 附录
    • 测试必检查项
        • 命名相关
        • 其它

前言

代码首先是给人看的,其次才是给机器执行的,因此一般情况下代码的可读性优先于性能,只有确定性能是瓶颈时,才需要主动优化。

可读性高的代码应当是易于理解并且易于实现的,代码越长越难看懂,可能出错的地方就越多,可靠性也越低。这就要求开发团队有一套统一的编程规范,根据清晰、简洁、风格统一的原则,来实现可靠性高,易于维护和重构的代码,对于C语言这种灵活度极高的语言来说更为重要。

一、标识符命名

现行的标识符命名规则有很多,典型的如:驼峰命名、匈牙利命名,各种风格孰优孰劣,业内也一直争论不休,归根到底这其实并不是一个客观的技术问题,更多的是和每个团队的习惯沿袭有关。

标识符命名同样要遵循清晰、简洁、风格统一的大原则,考虑到嵌入式产品主要基于Linux系统开发,为了保持代码风格的统一,我们选择使用小写字母加下划线作为标识符命名的UNIX风格。所有自研的C语言代码都应遵守本规范制定的规则,但在改动第三方代码时,除非整体改造,原则上应延续第三方原有的代码风格。

1.1 通用规则

1.1.1 标识符的命名要清晰、简明、有意义,可以使用完整的英语单词或是缩写,禁止使用单个字符和汉语拼音,避免让人产生误解。

使用单词缩写时应使用公认的缩写,常用的单词缩写可参考附录表格。如果代码中使用了特殊缩写,则需要在定义处给出必要的注释说明。

一个项目内专有名词应当统一,避免使用同意不同名的标识。

只允许定义i 、j、k等单个字符作为局部循环变量,其他情况则禁止使用。

uint32_t num;         /* ok */
uint32_t error_value; /* good */uint32_t aaa;         /* bad */
uint32_t shu_zhi;     /* bad */

1.1.2 尽量避免标识符中出现数字编号,除非逻辑上编号有实际意义。

uint8_t timer0; /* ok */uint8_t flag0; /* bad */

1.1.3 除头文件或编译开关等特殊标识符,不使用下划线“_”作为标识符的开头或结尾。

一般来说,以下划线开头或结尾的宏都是内部的定义,为避免冲突应使用单词作为标识符开头。

1.1.4 标识符的命名长度应当小于32个字符,以下划线“_”为间隔总字段不超过5段。

ISO C 标准要求内部标识符前31个字符必须是不同的,以保证移植性。过长的标识符可读性也较差,通常用5个字段足以准确描述变量或函数的意义。

1.2 文件命名

1.2.1 目录和文件使用小写字母和下划线命名,尽量不使用数字。

为了避免文件重名,尽量不要使用简单的高频词汇,可以使用模块+功能来命名文件。

1.2.2 模块对外接口的头文件统一使用“模块名_api.h”的格式命名。

一个功能模块可以包含若干源文件,允许使用一个公共的头文件作为接口文件。

1.3 宏和常量命名

1.3.1 宏和常量使用大写字母和下划线命名。

为了避免文件内部常量和外部变量重名,可以使用模块名作为常量的前缀。

#define GPIO_TEST_SWITCH 1 /* ok */const static uint8_t GPIO_MAX_NUM = 16; /* ok */

1.3.2 枚举值也是常量,同样使用大写字母和下划线命名。并使用模块名_ENUM_功能名形式命名

/* ok */
typedef enum 
{COLOUR_ENUM_RED;COLOUR_ENUM_BLUE;COLOUR_ENUM_GREEN;
} colour_enum_t;

1.4 结构体和枚举命名

1.4.1 结构体使用小写字母和下划线命名,统一使用typedef重命名,并以“_t”作为结构体名称的尾缀。

有一些规范要求结构体以“_s”作为结尾,枚举以“_e”作为结尾,用以区分彼此。但在实际操作中,结构体赋值往往需要跟踪到定义才知道具体的成员(现在的编辑器也会有快捷显示),而枚举一般很少使用枚举名,通常是直接使用枚举值,因而区分的收益不是很大。

1.4.2 结构体成员也使用小写字母和下划线命名。

成员名称应当尽量简短,明确结构体某一属性即可,不要重复结构体名称已经表明的信息,也不要出现明显和结构体意义不相关的成员名。

/* standard format, ok */
typedef struct tree_node 
{uint32_t data;struct tree_node *left;struct tree_node *right;
} binary_tree_t;/* general format, ok */
typedef struct 
{uint8_t age;uint8_t gender;uint8_t weight;uint8_t coat_color;
} cat_t;/* bad */
typedef struct 
{uint8_t cat_age;uint8_t cat_gender;uint16_t cat_weight;uint16_t dog_coat_color;
} cat_t;

1.4.3 枚举命名为模块名_enum_t。

1.4.4 对外使用结构体和枚举接口统一采用模块名+功能名的形式命名

/* bsp_gpio模块 */
struct bsp_gpio_config;
typedef struct bsp_gpio_config_t;

1.5 变量命名

1.5.1 变量使用小写字母和下划线命名,全局变量以“g_”作为前缀,静态变量以“s_”作为前缀,局部变量不加前缀。

不允许局部变量和全局变量重名,因此通过前缀加以区分,尽管局部变量和全局变量的作用域不同不会发生语法错误,但容易使人误解。同理,在函数内部不要定义同名的局部变量。

全局变量命名应该尽量详细,需要包含具体的模块名;局部变量命名则应该尽该量简约,说明用途或含义即可。

uint8_t g_timer_mode = 0; /* global variable, ok */
static uint8_t s_flag = 0;       /* static variable, ok */
uint8_t count = 0;        /* local variable, ok */

1.6 函数命名

1.6.1 函数使用小写字母和下划线命名,函数的名称应该体现函数的功能或动作。

常用操作的反义词参见附录,可作为命名参考。

1.6.2 函数指针以“func”作为名称尾缀,对外接口函数则以“模块名”作为名称前缀。

int32_t drive_flash_init(void);   /* ok */
void (*timer_timeout_func)(void); /* ok */

二、文件

文件设计是程序设计的重要一环,同样应当遵循“高内聚,低耦合”的设计原则。

2.1 源文件

2.1.1 文件职责应当单一,单个文件最大不得超过2000行。

2.1.2 每一个 .c 文件都应有一个同名 .h 文件,用于声明需要对外公开的接口。

如果一个.c文件不需要对外公布任何接口,则其就不应当存在,除非它是程序的入口,如main函数所在的文件。

2.1.3 一个模块可以包含多个 .c 文件,为方便外部使用,建议每一个模块提供一个 .h 。

需要注意的是,这个.h并不是简单的包含所有内部的.h,它是为了模块使用者的方便,对外提供的模块整体接口。

2.1.4 引用库文件通过<.h>方式引用,其他文件通过“.h”引用。引用顺序:C库(C++库)、第三方库、项目内头文件, 同一类型的头文件采用字母顺序排列。禁止包含用不到的头文件。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "activation.h"

2.1.5 只能通过包含头文件的方式使用其他 .c 提供的接口,禁止通过 extern 的方式使用外部函数接口或变量。

直接使用extern引用外部函数容易在外部函数改变时声明和定义变得不一致,同时也会使得代码整体架构变得离散不易维护。

extern uint8_t g_timer_mode;           /* bad */
extern int32_t drive_flash_init(void); /* bad */

2.2 头文件

2.2.1 头文件中只存放“声明”而不存放定义

头文件只能包含宏、枚举、结构体和函数的声明,且应该声明在唯一的头文件中,禁止在头文件中定义变量或实现函数。

头文件应该用于声明对象、函数、typedef 和宏,而不应该包含或生成占据存储空间的对象或函数(或它们的片断)的定义。这样就清洗划分了 .c 和 .h 文件的用途,即只有 .c 文件才包含可执行的源代码,而头文件只能包含声明。

2.2.2 仅在内部使用的宏、枚举、结构体和函数,不应放在头文件,而应该放在 .c 文件中

2.2.3 所有头文件都应采用#define宏防止重复包含,命名格式为“__文件名_H”,为保证唯一性推荐使用“__路径名_文件名_H”。

当头文件第一次被包含时会定义这个宏,在头文件被再次包含时将不再展开文件内容。

#ifndef __FTRMWARE_DRIVE_FLASH_H
#define __FTRMWARE_DRIVE_FLASH_H/* user code */#endif /* __FTRMWARE_DRIVE_FLASH_H */

2.2.4 头文件中段落安排顺序

/**
1、文件头注释
2、防止重复引用头文件的设置
3、包含头文件
4、外部宏定义
5、外部数据结构,包括 enum 常量声明,struct、union、typedef 等
6、接口数据声明
7、接口函数声明
8、文件尾注释
*/

示例:

/** ************************** Copyright (c) *********************************** * * * ------------------------------File Info-------------------------------------* @file:               file.h* @author:             作者* @date:               2024-12-6                                         * @version:            版本* @brief:              文件简介*                      * ----------------------------------------------------------------------------* @modified:            修改者* @date:               2024-12-6                                          * @version:            V0.0* @description:        修改功能简介* @note:               修改日志* ----------------------------------------------------------------------------* @copyright:          2024-2024 Company Name* ************************************************************************* */
#ifndef __FILE_H
#define __FILE_H
#ifdef __cplusplus
extern "C" {
#endif/** ****************************************************************************  @brief:包含头文件* ************************************************************************* */#include <stdint.h>#include <stdbool.h> 
/** ****************************************************************************  @brief:外部宏定义* ************************************************************************* *//** ****************************************************************************  @brief:外部数据结构* ************************************************************************* */
#pragma pack(1)                            //1字节对齐
#pragma anon_unions                        //开启匿名结构、联合#pragma no_anon_unions                     //关闭匿名结构、联合
#pragma pack()                             //取消字节对齐
/** ****************************************************************************  @brief:接口数据声明* ************************************************************************* *//** ---------------------------------------------------------------------------*  @brief:接口函数声明* ************************************************************************* */#ifdef __cplusplus
}
#endif#endif     // __FILE_H /** ****************************************************************************  End Of File*  在烧写的时候是FLASH中的被占用的空间为:      Code + RO-data + RW-data*  程序运行的时候,芯片内部RAM使用的空间为:     RW-data + ZI-data* ************************************************************************* */

2.2.5 在引用头文件时,不要使用绝对路径

如果使用绝对路径,当需要移动目录时,必须修改所有相关代码,繁琐且不安全;使用相对路径,当需要移动目录时,只需修改编译器的某个选项即可。例如:

#include “/project/inc/hello.h” /* 不应使用绝对路径 */
#include “../inc/hello.h”       /* 可以使用相对路径 */

2.2.6 在引用头文件时 ,使用<>还是""

#include <stdio.h>      /* 标准头文件 */
#include <projdefs.h>   /* 工程指定目录头文件 */
#include “global.h”     /* 当前目录头文件 */
#include “inc/config.h” /* 路径相对于当前目录的头文件 */

2.2.7 禁止头文件循环依赖

头文件循环依赖是指a.h包含b.h,b.h包含c.h,c.h包含a.h,导致修改任何一个头文件,都会使所有包含了a.h/b.h/c.h的代码全部重新编译一遍。而如果是单向依赖,如a.h包含b.h,b.h包含c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了b.h/c.h的源代码重新编译。

三、变量

3.1 数据类型

3.1.1 使用指示大小和符号的typedef 数据类型代替基本数据类型定义变量。

嵌入式系统或单片机型号驳杂,直接使用基本数据类型char、int、short、long、float 和double,无法保证代码的移植性。使用typedef重新定义基本数据类型,在切换平台时仅需调整typedef定义即可。

/* 32bit system */
typedef signed char    int8_t;
typedef signed short   int16_t;
typedef signed int     int32_t;
typedef signed long    int64_t;
typedef unsigned char  uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int   uint32_t;
typedef unsigned long  uint64_t;
typedef float          float32_t;
typedef double         float64_t;int32_t value; /* good */
int number;    /* bad */

或者加入标准头文件

#include <stdint.h>	  /* for uint8_t、uint32_t等 */
#include <stdbool.h>  /* for bool,true,false等 */

3.1.2 使用char 类型定义字符或字符串,int8_t 或uint8_t 类型只能用于定义数值或数组。

C语言中没有string类型,因此使用char定义string类型的数据。而且不论任何硬件平台,char类型都是一个字节,不存在长度兼容问题。

char str[] = "Hello World";      /* ok */
int8_t arr[5] = {1, 2, 3, 4, 5}; /* ok */
int8_t url[] = "www.xxxx.com";   /* bad */

3.1.3 尽量减少没有必要的数据类型默认或强制转换。

进行数据类型强制转换时,数据的意义、取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。

3.2 全局和局部变量

3.2.1 变量的使用要做到“名副其实”,一个变量只能有一个功能,不能把一个变量用作多种用途。

一个变量只能用来表示一个特定意义,当实际数据的意义和变量代表的意义不同时,应该定义一个新变量来承载数据,而不是为图方便依旧使用这个变量。除了难以理解外,还可能会引发逻辑上的错误。

uint32_t calculate_area(uint16_t length, uint16_t width)
{if ((0 == length) || (0 == width)) {return 0;}length *= width; /* bad */return length;}uint32_t calculate_area(uint16_t length, uint16_t width)
{if ((0 == length) || (0 == width)) {return 0;}uint32_t area = length * width; /* good */return area;}

3.2.3 尽量不用或者少用全局变量,且全局变量不能作为对外的接口使用。

全局变量可以认为是模块的私有数据,在单个文件内定义全局变量时使用static修饰,以防止外部文件的非正常访问。

确实需要外部操作全局变量时,应提供接口函数来设置、获取,同时要注意全局数据的访问互斥。

3.2.4 所有变量在使用前都必须初始化,严禁使用未经初始化的变量作为右值。

根据ISO C 标准,static修饰的变量缺省地被自动赋予零值,除非经过了显式的初始化。实际中,一些嵌入式环境没有实现这样的缺省行为,因此不论全局变量或局部变量都应初始化后再使用。

#define DATA_BUF_SIZE 10static uint8_t g_data_buf[DATA_BUF_SIZE] = { 0 }; /* good */uint8_t get_data_num(void)
{uint8_t num = 0; /* good */uint8_t index;   /* bad */while (index < DATA_BUF_SIZE) {if (g_data_buf[index] > 0) {num++;}index++;}return num;
}void set_data_buf(uint8_t index, uint8_t data)
{if (index >= DATA_BUF_SIZE) {return;}uint8_t temp = 0;         /* not good */temp = g_data_buf[index]; /* uint8_t temp = g_data_buf[index]; ok */if (data > temp) {temp = data - temp;}g_data_buf[index] = temp;return;}

3.2.5 明确全局变量的初始化顺序,尽量避免跨模块的初始化依赖,确实有依赖时要保证正确的时序关系。

例如系统初上电时,业务模块的全局变量依赖文件模块从flash读取的数据进行初始化,则在使用业务模块全局变量前必须保证文件模块读取完成。

3.2.6 应使用大括号以指示和匹配数组及结构体的非0初始化;初始化为0或NULL时,只用一层大括号即可。

ISO C 要求数组、结构体和共用体的初始化列表要以一对大括号括起来(尽管不这样做的行为是未定义的)。本规则更进一步地要求,使用附加的大括号来指示嵌套的结构,迫使程序员显式地考虑和描述复杂数据类型元素的初始化次序。

int16_t arr1[2][2] = { 1, 2, 3, 4 };         /* bad */
int16_t arr2[2][2] = { { 1, 2 }, { 3, 4 } }; /* good */
int16_t arr3[2][2] = {0};                  /* ok */

3.2.7 结构功能单一,不要设计面面俱到的数据结构。

结构的定义应该可以明确的描述一个对象,设计结构时应力争使结构代表一种现实事务的抽象,而不是同时代表多种。结构中的各元素应代表同一事务的不同侧面,而不应把描述没有关系或关系很弱的不同事务的元素放到同一结构中。

3.2.8 通信过程中使用的结构,必须注意字节序。

处理器大小端、位数不同,数据结构的字节序差别会很大,所以跨平台数据交互前,都应进行必要的字节序转换以符合通信要求。

四、宏和常量

4.1 宏定义

4.1.1 应尽可能使用函数代替宏,减少用宏定义表达式。

宏对比函数,有一些明显的缺点:

宏缺乏类型检查,不如函数调用检查严格
宏形式的代码难以打断点调试,不利于定位问题
宏如果调用的很多,会造成代码空间的浪费,不如函数空间效率高
宏展开后可能会产生意想不到的副作用

#define SQUARE(a) (a) * (a)
SQUARE(i++); /* 展开后 (i++) * (i++) i被加两次*/uint32_t square(uint16_t a) 
{uint32_t result = a * a; return result; 
}
square(i++); /* i只加一次*/

4.1.2 确实需要用宏定义表达式时,要使用完备的括号,并且不允许参数发生变化。

#define SUM(a, b) a + b       /* bad */
#define SUM(a, b) (a + b)     /* bad */
#define SUM(a, b) (a) + (b)   /* bad */#define SUM(a, b) ((a) + (b)) /* ok */

4.1.3 宏定义有多条语句时要放在大括号中,最多不超过10行,不允许使用 return、goto、continue、break等改变程序流程的语句。

#define CHECK_PARA_VALID(para) {if (para == NULL) {return ret;}} /* bad */

4.1.3 宏定义末尾不要跟分号“;”。

分号不是宏定义的必要组成部分,如果宏定义末尾跟了分号,调用宏时不写分号不符合一般C语句的书写习惯,写分号则重复,更要命的是有些情况下还会导致语法错误。

#define SUM(a, b) ((a) + (b)); /* bad */uint8_t v1 = 1;
uint8_t v2 = 2;
uint8_t v3 = 3;
uint8_t v4 = 4;
uint8_t result = SUM(v1, v2) + SUM(v3, v4); /* error ((1) + (2)); + ((3) + (4)); */

4.1.4 不允许使用#undef。

使用#undef会使宏的存在或含义产生混乱。

4.2 常量

4.2.1 常量使用十进制或十六进制表示,禁止使用八进制常量。

#define BUF_MAX_SIZE 100                      /* ok */
static uint8_t g_buf_len = 0;                 /* ok */
static uint8_t g_buf[BUF_MAX_SIZE]= { 0x00 }; /* ok */
static uint8_t g_buf_index = 01;              /* bad */

4.2.2 不允许直接使用魔鬼数字。

uint8_t month = 12; /* December or 12 months ? */

所谓魔鬼数字是指程序中出现的难以理解的数字。魔鬼数字除了使代码难以理解外,如果这个数字在程序中多处使用,很难统一修改。因此:

多处使用的数字,必须定义const全局变量或宏,变量名或宏命名应当自注释数字的意义
集中在局部使用的数字,可以在代码周围增加说明注释,也可以定义局部const变量,变量命名自注释
0作为一个特殊的数字,作为一般默认值使用没有歧义时不用特别定义
4.2.3 在枚举列表中,“=”不能显式用于除首元素之外的元素上,除非所有的元素都是显式初始化的。

/* ok */
typedef enum 
{MONDAY = 1,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,   
} week_t;/* bad */
typedef enum 
{MONDAY,TUESDAY = 2,WEDNESDAY = 3,THURSDAY,FRIDAY,   
} week_t;

五、函数

函数设计要求代码简单直接,用合适的数据结构和直截了当的控制语句将函数有机的组织起来。

5.1 函数体

5.1.1 函数功能要单一,即一个函数只实现一个功能。

一个函数实现多个功能给开发、使用、维护都带来很大的困难。将没有关联或者关联很弱的语句放到同一函数中,会导致函数职责不明确,难以理解,难以测试和改动。

5.1.2 重复代码应该尽可能提炼成函数。

重复代码提炼成函数可以降低维护成本和资源消耗,并提高代码的复用性。

5.1.3 为了函数的可读性和维护性,应避免函数过长,有效代码行数一般不超过 50 行,对于逻辑联系特别紧密或层次清晰的可放宽至100行。

过长的函数往往意味着函数功能不单一,应该考虑重新设计函数结构,使功能内聚行数减少。

有效代码行指非空非注释的代码行。

限制函数行数目的在于提升代码的可读性,但有些场景下代码内在逻辑联系特别紧密或层次简单,拆开反而影响理解,因而可以放宽行数限制,不要“为拆而拆”。

5.1.4 为避免函数的代码块嵌套过深,函数的代码块嵌套不超过4层。

函数的代码块嵌套深度指的是函数中的代码控制块(例如:if、for、while、switch等)之间互相包含的深度。每级嵌套都会增加阅读代码时的脑力消耗,因为需要在脑子里维护一个“栈”。应当优先使用卫语句,在不满足条件时直接退出函数实现“短路”操作,以减少代码的嵌套层级。

/* bad */
void usart1_irq_handler(void)
{uint8_t res;if (USART1->SR) {res = USART1->DR;if ((USART_RX_STA & 0x8000) == 0) {if ((USART_RX_STA & 0x4000) == 0) {if (res == 0x0d) {USART_RX_STA |= 0x4000;} else {if (USART_RX_STA > (USART_REC_LEN - 1)) {USART_RX_STA = 0; /* 5层嵌套 */}}}}}
}/* ok */
void usart2_irq_handler(void)
{uint8_t res;if (!USART2->SR) {return;}res = USART2->DR;if (((USART_RX_STA & 0x8000) != 0) || ((USART_RX_STA & 0x4000) != 0)) {return;}if (res == 0x0d) {USART_RX_STA |= 0x4000;} else {if (USART_RX_STA > (USART_REC_LEN - 1)) {USART_RX_STA = 0;}}
}

5.1.5 在源文件范围内声明和定义的所有函数,除非外部可见,否则都应使用static修饰。

如果一个函数只是在同一文件内调用,那么就用static修饰,避免和其他文件中的相同标识符发生混淆。

5.1.6 非调度函数应避免使用共享变量,不可避免的地方应集中使用;可重入函数应避免使用共享变量,若确实需要使用,则应通过互斥手段(关中断、信号量)对其加以保护。

调度函数是指根据输入的消息或命令,启动相应的函数或过程,而本身并不完成具体功能的函数。

可重入函数是指可能被多个任务并发调用的函数。在多任务操作系统中,函数具有可重入性是多个任务可以共用此函数的必要条件。

共享变量指的全局变量和static变量。

功能函数应尽量通过操作参数或局部变量来实现具体功能,各个任务会维护自己的栈空间,数据不会彼此影响。

5.2 参数和返回值

5.2.1 模块接口函数要对入参的合法性做全面检查,模块内部使用的函数对入参的合法性做安全检查即可。

供外部调用的接口函数,其入参是不可信的,需要对参数做全面的检查保证数据有效并符合取值范围。

模块内部使用的函数入参一般已经过滤,通常认为是可信的,过度的参数检查会造成大量冗余代码,因此只要在使用前判断指针不为空、除数不为零等安全条件即可。

5.2.2 函数的不变参数使用const 修饰。

使用const修饰不变的入参,在编译时会对其进行检查,避免函数内部误操作导致的错误,使代码更安全。

void* memcpy(char *strDest, const char *strSrc, int Count); /* good */

5.2.3 函数的参数个数最多不超过5个,参数中有数组指针的,一定要加数组长度的参数。

5.2.4 不带参数的函数应当声明为void 类型的参数。

int32_t hal_timer_init();     /* bad */
int32_t hal_timer_init(void); /* good */

5.2.5 函数参数应该使用确定的类型,尽量避免使用通用的void *类型,通用类型不利于参数检查。

5.2.6 函数不要调用另一个函数作为参数使用,这不利于代码阅读和调试。

func1(func2(), func3()); /* bad */

5.2.7 除具有打印功能的函数外,不得定义变参函数。

变参存在许多潜在的问题,仅允许在打印日志相关的函数中使用stdarg.h、va_arg、va_start 和va_end。

5.2.8 除特定的算法函数外,禁止函数递归调用,使用递归时要有确定退出条件。

5.2.9 void 返回类型的函数也需要显式的return 语句,以表明函数结束。

void hal_adc_init(void)
{/* code */return;
}

5.2.10 对函数的错误码做全面处理。

一个函数(标准库中的函数/第三方库函数/用户定义的函数)能够提供一些指示错误发生的方法。可以是错误标记变量、特殊的返回值或者其他手段,只要函数提供了这样的机制,调用程序都应该在函数返回时立刻检查错误指示。

六、语句和表达式

6.1 表达式

6.1.1 用括号明确表达式的操作顺序,避免过分依赖默认优先级。

使用括号强调所使用的操作符,防止因默认的优先级与设计思想不符而导致程序出错,然而过多的括号会分散代码降低可读性,因此不要并列过多的条件。

6.1.2 禁止在表达式中进行赋值操作及其他有副作用的操作。

/* bad */
uint8_t count = 0;
if (count++ < 10) 
{/* code */
}

6.1.3 不允许使用逗号运算符。

使用逗号运算符会降低代码的可读性,可以使用其他方法达到相同的效果。

6.1.4 不允许使用三目运算符。

三目运算符确实可以在形式上简化代码,但嵌套使用会导致程序恶化,而且其执行顺序由编译器决定,其语义又可以用if else 语句替换,没必要为了单纯减少代码行数使用该运算符。

6.1.5 除数不能为零;指针变量使用前要NULL进行判空;无符号数做减法,减数不能大于被减数。

这些都是导致程序异常甚至死机的错误,在程序中一定要避免。

6.1.6 除非操作数是布尔类型,否则应当显示的表明表达式的判断条件。

/* bad */
int32_t num = 0;
if (num) 
{ /* code */
}/* ok */
if (num != 0) 
{ /* code */
}/* ok */
bool flag = flase;
if (flag) 
{ /* code */
}

6.1.7 浮点表达式不能做相等或不等的检测。

这是浮点类型的固有特性,等值比较通常不会计算为true。而且,这种比较行为不能在执行前做出预测,它会随着实现的改变而改变。

6.2 语句

6.2.1 for 语句的三个表达式应该只关注循环控制,不应该存在其他副操作。

for 语句的三个表达式都存在时,它们应该只用于如下目的:

第一个表达式 初始化循环计数器
第二个表达式 对循环计数器和其他值的比较
第三个表达式 循环计数器递增或递减,不能在循环体中修改循环计数器
6.2.2 使用else if 语句时,最后必须加else 分支。

/* ok */
if (count == 0) 
{/* code */
} 
else if (count > 0) 
{/* code */
} 
else 
{ /* nothing */
}

6.2.3 使用switch 语句时,最后必须加default 分支,case 后不跟break 的要注释说明。

switch (cmd) 
{case RT_TIMER_CTRL_GET_TIME:*(rt_tick_t *)arg = timer->init_tick;break;case RT_TIMER_CTRL_SET_PERIODIC:timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;break;case RT_TIMER_CTRL_GET_STATE: /* example */case RT_TIMER_CTRL_GET_REMAIN_TIME:*(rt_tick_t *)arg =  timer->timeout_tick;break;default: /* nothing */break;
}

else if 语句最后加else 语句,switch 语句中最后加default 的目的是要求程序做保护性编程,应执行适当动作保证程序安全,即使没有执行动作也要添加注释以说明原因。

6.2.4 仅允许在函数体内使用goto 语句,用于出现错误时统一处理,其他情况则禁止使用goto 。

/* ok */
void func1(void)
{if (!func2()) {goto EXIT;}if (!func3()) {goto EXIT;}EXIT:func4();return;
}

6.2.5 不允许代码中存在执行不到的语句。

程序中执行不到的代码或未使用的变量不仅占用额外的空间,还会影响程序的可读性和性能,应当删除。

七、注释

7.1 要求

7.1.1 应当力求减少注释,最多不要超过代码量的30%,优秀的代码可以自我解释,不通过注释也可轻易读懂。

注释无法把糟糕的代码变好,需要很多注释来解释的代码往往存在坏味道,需要重构。

7.1.2 注释要解释代码难以直接表达的意图,而不是重复描述代码。

注释的目的是解释代码的目的、功能和采用的方法,提供代码以外的信息,帮助读者理解代码。注释不是为了名词解释,而是为了说明用途。

7.1.3 修改代码时要同时修改其注释,以保证注释与代码的一致性,不用的注释要及时删除。

7.1.4 注释应放在其代码上方相邻位置或右方,禁止放在其他位置。

7.1.5 考虑程序的实际使用者,建议尽量使用中文进行注释。

7.1.6 不允许程序中存在注释掉的代码。

不使用的代码应当及时删掉,具有兼容性质的代码应当使用宏控,而不是注释掉。

7.2 格式

7.2.1 使用 /* */ 进行多行注释,当用作单行注释时,注释内容左右各留一个空格。

/* 单行注释 *//**
多行注释
*/

7.2.2 使用//进行单行注释时,可新开一行,且缩进量与代码保持一致

//系统初始化
printf("System Init.\r\n");

7.2.2 在文件开头顶格注释版权声明,并简要概述文件作用或实现的功能,并记录作者名和创建日期。

修改文件内容时,仅修改声明的日期即可,具体的修改内容应该通过版本工具进行记录。只有涉及大的功能变更时才需修改说明,但此时应该优先考虑增加新文件,而不是在原文件上大改。

/** ************************** Copyright (c) *********************************** * * * ------------------------------File Info-------------------------------------* @file:               file.c* @author:             作者* @date:               2024-12-6                                         * @version:            版本* @brief:              文件简介* * ----------------------------------------------------------------------------* @modified:            修改者* @date:               2024-12-6                                          * @version:            V0.0* @description:        修改功能简介* @note:               修改日志* ----------------------------------------------------------------------------* @copyright:          2024-2024 Company Name* ************************************************************************* */

7.2.3 接口函数声明的注释要描述函数功能及用法,包括输入和输出参数、返回值和调用要求等。

不要为所有的函数都写注释,只在重要的、复杂的函数定义处注释其功能和实现要点即可。

/** **************************************************************************** @name:              func_name * @brief              函数功能简介* @param              参数1* @param              参数2* @retval             返回值* @author:            ${author.body}* @date:              2024-12-06* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          修改者* @date:              2024-12-06* @version:           V0.0* @description:       修改功能简介* @note:              修改日志* ************************************************************************* */

八、排版与格式

8.1 通用格式

8.1.1 代码文件统一用GB2312 编码格式保存。

尤其是代码中存在中文时,使用GB2312 编码可以避免在不同编辑器上显示乱码的问题。

某些编辑器在采用UTF-8编码时,输入数据的时候可能会出现异常。

8.1.2 除函数定义外,语句块左大括号紧跟前行, 每级缩进为4个空格,不允许使用Tab 缩进。

大体上遵循K&R 风格,实现紧凑的代码结构,提高阅读效率,但K&R 风格的缩进是8个空格,过于冗长,使用4个空格足已,这也是其他主流编程语言的通用规范。

在编写代码时可以将编辑器的Tab 键设置为4个空格以简化操作。

8.1.3 相对独立的程序块之间、变量说明后加空行

void demo_func(void)
{uint8_t i;/* 功能块1 */for (i = 0; i < 10; i++){/* 代码块1 */}/*不同的功能块之间空一行*/ /* 功能块2 */for (i = 0; i < 20; i++){/* 代码块2 */}
}

8.1.4 较长的语句或函数过程参数(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。

if ((para_1 == 0) && (para_2 == 0) && (para_3 == 0)|| (para_4 == 0))
{}

8.1.5 if 、for 、do 、while 、case 、switch 、default 等语句需要独占一行。

8.1.6 if 、for、else、while 等语句即使只有一行内容甚至没有内容,也要写{},不允许省略。

/* ok */
typedef struct 
{uint8_t age;uint8_t gender;
} cat_t;if (num == 0) 
{/* code */
} 
else if (num > 0) 
{/* code */
} 
else 
{/* code */
}while (true) 
{/* code */
}do 
{/* code */
} while (i < 5);for (uint8_t i = 0; i < 10; i++) 
{/* code */
}switch (cmd) 
{case CMD1:/* code */break;case CMD2:/* code */break;default:/* code */break;
}void func (void)
{/* code */return;
}

8.1.7多个短语句不允许写在同一行内 ,即一行只写一条语句。

/* bad */
int32_t a, b, c;
int32_t a; int32_t b;
int32_t a = 0; int32_t b;/* ok */
int32_t a = 0;
int32_t b;

8.1.8 程序块的分界符(如大括号‘{’和‘}’ )应各独占一行并且位于同一列

/* bad */
for (...){/* 代码块 */
}/* ok */
for (...)
{/* 代码块 */
}/* bad */
if (...){/* 代码块 */}/* ok */
if (...)
{/* 代码块 */
}

8.1.9 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符前、后要加空格 ; 对于关系密切的立即操作符(-> . ),则不加空格;指针变量 统一跟随变量,在类型和 间加空格。**

示例:

  • 逗号、分号只在后面加空格。

    int32_t a, b, c;
    
  • 比较操作符,赋值操作符"=“、 “+=”,算术操作符”+“、”%“,逻辑操作符”&&“、”&“,位域操作符”<<“、”^"等双目操作符的前后加空格。

    if (current_time >= MAX_TIME_VALUE)
    {a = b + c;
    }
    a *= 2;
    a = b ^ 2;
    
  • “!”、“~”、“++”、“–”、“&”(地址运算符)等单目操作符前后不加空格。

    *p = 'a';            /* 内容操作"*"与内容之间*/
    flag = !is_empty;    /* 非操作"!"与内容之间*/
    p = &mem;            /* 地址操作"&"与内容之间*/
    i++;                /* "++","--"与内容之间*/
    
  • “->”、"."前后不加空格。

    p->id = pid;        /* "->"指针前后不加空格 */
    
  • if、for、while、switch 等与后面的括号间应加空格,使 if 等关键字更为突出、明显,函数名与其后的括号之间不加空格,以与保留字区别开。

    if (a >= b && c > d)
    
  • 对于已经很清晰的语句没有必要再加空格,如括号内侧(即左括号后面和右括号前面)不需要加空格,多重括号间不必加空格。

    /* ok */
    uint16_t arr[][] = {{1, 2, 3}, {4, 5, 6}};
    

8.1.10 预处理和宏语句必须顶格写,不允许在# 前出现其他字符

虽然语法要求预处理指令前可以有空格,但在操作中应避免这样的写法,即使预处理在语句块中也要顶格写,不受缩进限制。

8.1.11 #define不能在语句块中进行定义,必须放在文件开头。

尽管在C代码中的任何位置放置#define 都是合法的,但放在语句块中会使人误以为该宏仅在该语句块起作用。#define 指令要放在第一个函数定义之前。

8.1.12 注释在上方的,需要与当前代码块保持缩进一致;在右侧的,在仅有一个注释的时候,注释与语句留一个空格; 若多行语句均需注释,则注释与最长语句留一个空格,其他注释与之对齐。

8.2 文件示例

  • 头文件
/** ************************** Copyright (c) *********************************** * * * ------------------------------File Info-------------------------------------* @file:               date.h* @author:             fancyang* @date:               2020-01-13                                          * @version:            V0.1* @brief:              时间片处理机模块*                      * ----------------------------------------------------------------------------* @modified:           fancyang* @date:               2024-12-6                                          * @version:            V0.1* @description:        统一接口名称* @note:               V0.0 创建文件V0.1 统一修改接口名称* ----------------------------------------------------------------------------* @copyright:          2024-2024 Company Name* ************************************************************************* */
#ifndef __DATE_H
#define __DATE_H
#ifdef __cplusplus
extern "C" {
#endif/** ****************************************************************************  @brief:包含头文件* ************************************************************************* */#include <stdint.h>#include <stdbool.h> 
/** ****************************************************************************  @brief:外部宏定义* ************************************************************************* *//** ****************************************************************************  @brief:外部数据结构* ************************************************************************* */
#pragma pack(1)                            //1字节对齐
#pragma anon_unions                        //开启匿名结构、联合typedef enum
{DATE_TIME_ENUM_STANDBY         = 0x00,DATE_TIME_ENUM_BEGIN         = 0x01,DATE_TIME_ENUM_RUN             = 0x02,DATE_TIME_ENUM_STOP             = 0x03,DATE_TIME_ENUM_STORED         = 0x04,
}date_time_enum_t;typedef struct
{uint8_t time_state;uint32_t timer_count;uint8_t hour;uint8_t minute;uint16_t second;uint32_t ms;
}date_t;typedef struct
{uint16_t hour;uint8_t minute;uint16_t second;}date_time_t;#pragma no_anon_unions                     //关闭匿名结构、联合
#pragma pack()                             //取消字节对齐
/** ****************************************************************************  @brief:接口数据声明* ************************************************************************* *//** ---------------------------------------------------------------------------*  @brief:接口函数声明* ************************************************************************* */
/*系统运行时间状态机控制*/
void date_run_time_control(void);
/*系统时钟计算,中断中1ms调用一次*/
void date_system_tick_count_run(void);
/*时间结构体处理,自加时分秒*/
void date_timer_process(date_t * timer);
/*取系统时间片*/
date_t date_get_system_timer(void);
/*设系统时间片*/
void date_set_system_timer(date_t system_timer);
/*取工作时间*/
date_t date_get_system_timer(void);
/*设工作时间*/
void date_set_work_time(date_time_t work_time);
/*取运行时间*/
date_time_t date_get_run_time(void);
/*设运行时间*/
void date_set_run_time(date_time_t run_time);
/*取时间状态机*/
date_t date_get_state_timer(void);
/*设时间状态机*/
void date_set_state_timer(date_t state_timer);
/*取系统上电运行时间*/
date_time_t date_get_power_on_run_time(void);
/*毫秒级时间片到函数*/ 
bool date_is_time_up(uint32_t nms,bool is_tick_need_set);
#ifdef __cplusplus
}
#endif#endif     // __FILE_H /** ****************************************************************************  End Of File*  在烧写的时候是FLASH中的被占用的空间为:      Code + RO-data + RW-data*  程序运行的时候,芯片内部RAM使用的空间为:     RW-data + ZI-data* ************************************************************************* */
  • 源文件
/** ************************** Copyright (c) *********************************** * * * ------------------------------File Info-------------------------------------* @file:               date.c* @author:             fancyang* @date:               2020-01-13                                          * @version:            V0.1* @brief:              时间片处理机模块*                      * ----------------------------------------------------------------------------* @modified:           fancyang* @date:               2024-12-6                                          * @version:            V0.1* @description:        统一接口名称* @note:               V0.0 创建文件V0.1 统一修改接口名称* ----------------------------------------------------------------------------* @copyright:          2024-2024 Company Name* ************************************************************************* */
#include "date.h"static volatile date_t s_state_timer = {0} ;        //时间结构体 static volatile date_t s_system_timer;        //系统本次开机运行时间 static volatile date_time_t s_work_time = {0};        //系统总运行时间static volatile date_time_t s_run_time = {0};            //系统总升降时间static volatile date_time_t s_power_on_s_run_time = {0};    //系统本次开机升降时间static volatile uint32_t s_time_up_tick;        //系统时间片到计数/** **************************************************************************** @name:              date_get_system_timer* @brief:             取系统时间片* @param:             * @retval:            date_t* @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
date_t date_get_system_timer(void)
{return s_system_timer;
}/** **************************************************************************** @name:              date_set_system_timer* @brief:             设系统时间片* @param:             * @retval:            date_t* @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
void date_set_system_timer(date_t system_timer)
{s_system_timer = system_timer;
}/** **************************************************************************** @name:              date_get_work_time* @brief:             取工作时间* @param:             * @retval:            date_t* @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
date_time_t date_get_work_time(void)
{return s_work_time;
}/** **************************************************************************** @name:              date_set_work_time* @brief:             设工作时间* @param:             date_t work_time* @retval:            * @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
void date_set_work_time(date_time_t work_time)
{s_work_time = work_time;
}/** **************************************************************************** @name:              date_get_run_time* @brief:             取运行时间* @param:             * @retval:            date_time_t* @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
date_time_t date_get_run_time(void)
{return s_run_time;
}/** **************************************************************************** @name:              date_set_run_time* @brief:             设运行时间* @param:             date_time_t run_time* @retval:            * @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
void date_set_run_time(date_time_t run_time)
{s_run_time = run_time;
}/** **************************************************************************** @name:              date_get_system_timer* @brief:             取运行状态* @param:             * @retval:            date_t* @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
date_t date_get_state_timer(void)
{return s_state_timer;
}/** **************************************************************************** @name:              date_get_system_timer* @brief:             设运行状态* @param:             date_t state_timer* @retval:            * @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
void date_set_state_timer(date_t state_timer)
{s_state_timer = state_timer;
}/** ************************************************************************************ @name:              date_timer_process* @brief:             时间处理* @param:             * @retval:            date_t* @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ------------------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ********************************************************************************* */
void date_timer_process(date_t * timer)
{if (timer->ms == 1000){timer->ms = 0;timer->second++; }if (timer->second == 60){timer->second = 0;timer->minute++;}if (timer->minute == 60){    timer->minute = 0;timer->hour++;}
}/** **************************************************************************** @name:              date_system_tick_count_run* @brief:             系统时钟计算,中断中1ms调用一次* @param:             * @retval:            date_t* @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
void date_system_tick_count_run(void)
{s_system_timer.timer_count++;s_system_timer.ms++;if (s_system_timer.ms == 1000){s_system_timer.ms = 0;s_system_timer.second++;s_work_time.second++;}if (s_system_timer.second == 60){s_system_timer.second = 0;s_system_timer.minute++;}if (s_system_timer.minute == 60){    s_system_timer.minute = 0;s_system_timer.hour++;}if (s_work_time.second == 60){s_work_time.second = 0;s_work_time.minute++;}if (s_work_time.minute == 60){    s_work_time.minute = 0;s_work_time.hour++;}if (s_power_on_s_run_time.second > 60){s_power_on_s_run_time.minute += s_power_on_s_run_time.second / 60;s_power_on_s_run_time.second = s_power_on_s_run_time.second % 60;}if (s_power_on_s_run_time.second == 60){s_power_on_s_run_time.second = 0;s_power_on_s_run_time.minute++;}if (s_power_on_s_run_time.minute == 60){    s_power_on_s_run_time.minute = 0;s_power_on_s_run_time.hour++;}if (s_run_time.second > 60){s_run_time.minute += s_run_time.second / 60;s_run_time.second = s_run_time.second % 60;}if (s_run_time.second == 60){s_run_time.second = 0;s_run_time.minute++;}if (s_run_time.minute == 60){    s_run_time.minute = 0;s_run_time.hour++;}if (s_system_timer.timer_count > 100000000){s_system_timer.timer_count = 0;}}/** **************************************************************************** @name:              date_run_time_control* @brief:             系统运行时间状态机控制* @param:             * @retval:            * @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
void date_run_time_control(void)
{if (s_state_timer.time_state == DATE_TIME_ENUM_STANDBY){}if (s_state_timer.time_state ==  DATE_TIME_ENUM_BEGIN){s_state_timer.time_state =  DATE_TIME_ENUM_RUN;s_state_timer.second = 0;s_state_timer.timer_count = s_system_timer.timer_count;}                    if (s_state_timer.time_state == DATE_TIME_ENUM_RUN){s_state_timer.second = (s_system_timer.timer_count - s_state_timer.timer_count) / 1000;} if (s_state_timer.time_state == DATE_TIME_ENUM_STOP){s_state_timer.time_state = DATE_TIME_ENUM_STORED;s_run_time.second += s_state_timer.second;s_power_on_s_run_time.second += s_state_timer.second;}if (s_state_timer.timer_count > 10000000){    s_state_timer.timer_count = 0;}}/** **************************************************************************** @name:              date_is_time_up* @brief:             毫秒级时间片到函数* @param:             * @retval:            * @author:            fancyang* @date:              2020-01-13* @version:           V0.0* ----------------------------------------------------------------------------* @modified:          * @date:              时间: 2023-11-18* @version:           V0.0* @description:       * @note:              * ************************************************************************* */
bool date_is_time_up(uint32_t nms,bool is_tick_need_set)
{if (s_system_timer.timer_count -  s_time_up_tick > nms){if (is_tick_need_set){s_time_up_tick = s_system_timer.timer_count;}return true;}return false;
}/** ****************************************************************************  End Of File*  在烧写的时候是FLASH中的被占用的空间为:      Code + RO-data + RW-data*  程序运行的时候,芯片内部RAM使用的空间为:     RW-data + ZI-data* ************************************************************************* */

附录

附表1 常用单词缩写

argument / argbuffer / bufclock / clkcommand / cmdcompare / cmpconfiguration / cfgdevice / dev
error / errhexadecimal / hexincrement / incinitialize / initmaximum / maxmessage / msgminimum / min
parameter / paraprevious / prevregister / regsemaphore / semstatistic / statsynchronize / synctemp / tmp

附表2 常用反义词组

add/removebegin/endcreate/destroyinsert/deletefirst/lastget/setincrement/decrement
input/outputcreate/deletelock/unlockopen/closemin/maxold/newstart/stop
next/previoussource/targetshow/hidesend/receivesource/destinationcopy/pasteup/down

参考资料

嵌入式C语言编程规范

华为C&C++ 语言编程规范

阿里C 语言规范

MISRA-C 编程规范

Mbed TLS 编码规范

测试必检查项

请注意,测试人员会严格检查下述内容项。

命名相关
  1. 文件名:使用小写字母和下划线命名,不使用数字,如example_case.c;
  2. 宏和常量:使用大写字母和下划线命名,如EXAMPLE_CASE;
  3. 变量使用小写字母和下划线命名,如example_case;
  4. 结构体、枚举、联合体使用小写字母和下划线命名,如example_case;
  5. 函数使用小写字母和下划线命名,如example_case;
其它
  1. 宏、常量、枚举、联合体、结构体、函数等需加模块的前缀名;
  2. 只允许定义i 、j、k等单个字符作为局部循环变量;
  3. 结构体、枚举、联合体尾缀_t;
  4. 全局变量加_g,静态变量加_s;
  5. 添加文件注释,函数注释,描述正确、准确;
http://www.lryc.cn/news/573887.html

相关文章:

  • Vue3解析Spring Boot ResponseEntity
  • select和poll用法解析
  • 如何仅用AI开发完整的小程序<4>—小程序页面创建与删除
  • 软件工程核心知识全景图:从需求到部署的系统化构建指南
  • 《算法笔记》之二(笔记)
  • DeepSeek:中国AI开源先锋的技术突破与行业革新
  • DeepSeek技术解析:开源大模型的创新突围之路
  • Unity中的Mathf.Clamp
  • 【unitrix】 4.0 类型级数值表示系统(types.rs)
  • 【已解决】 数据库INSERT操作时,Column count doesn’t match value count at row 1
  • 微处理器原理与应用篇---常见基础知识(6)
  • Redis-CPP 5大类型操作
  • 72、单元测试-常用测试注解
  • vue3 el-table 行字体颜色 根据字段改变
  • 在 Windows 和 Linux 下使用 C/C++ 连接 MySQL 的详细指南
  • SQL 中 HAVING COUNT (1)>1 与 HAVING COUNT (*)>1 的深度解析
  • Spring Boot Actuator 跟踪HTTP请求和响应
  • 开源 python 应用 开发(二)基于pyautogui、open cv 视觉识别的工具自动化
  • Python 的内置函数 help
  • python 常见数学公式函数使用详解
  • oracle rac - starwind san 磁盘共享篇
  • 【闲谈】对于c++未来的看法
  • Java面试复习:面向对象编程、JVM原理与Java 8新特性
  • Flink源码阅读环境准备全攻略:搭建高效探索的基石
  • Go语言--语法基础6--基本数据类型--数组类型(1)
  • 114. 二叉树展开为链表
  • C++插值记录
  • 开发云数据库
  • Python环境搭建竞赛
  • python的高校教师资源管理系统