




已阅读5页,还剩24页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
ICS35.060L74中华人民共和国国家标准GB/T XXXXXXXXX嵌入式软件 C语言编码规范Embedded Software C Language Coding Specification“在提交反馈意见时,请将您知道的相关专利连同支持性文件一并附上”(本稿完成日期:2009-12-23)XXXX - XX - XX发布XXXX - XX - XX实施GB/T XXXXXXXXX前言本标准由全国信息技术标准化技术委员会(SAC/TC28)提出并归口。本标准起草单位:中国电子技术标准化研究所、珠海南方软件产品检测中心、炬力集成电路设计有限公司本标准主要起草人:引言嵌入式系统是指以应用为中心,以计算机技术为基础,软硬件可裁剪,适应应用系统对功能、可靠性、成本、体积和功耗严格要求的专门计算机系统。嵌入式技术并不是一个独立的学科,它是伴随着微电子技术和计算机技术的发展,微控制芯片功能越来越强大,而嵌入微控制芯片的设备和系统越来越多而发展起来的。嵌入式系统几乎包括了生活中所有的电器设备,如:mp3、手机、数字电视、汽车、微波炉、数码相机、电梯、空调、自动售货机、工业自动化仪表与医疗仪器等。尽管C语言是嵌入式软件开发的主要工具,然而C语言并非是专门为嵌入式系统设计的,一是因为标准C语言编写的软件缺乏安全性;二是因为“标准C语言”太庞大了,很难操作。因此,需要制订针对嵌入式系统软件的编码格式要求。本标准是在理解标准C语言的基础上,结合嵌入式软件的开发实践以及嵌入式软件开发中常见的危险编码方式制订的,着重于软件的安全性、可读性。既可作为嵌入式软件开发中的编码要求,也可作为软件交付时验收方的验证要求。在要求条款中,“应”标示的要求是必须符合的,“宜”标示的要求是可选择符合的。26嵌入式软件 C语言编码规范1 范围本标准规定了使用C语言编写嵌入式软件的编码格式要求。本标准也提及了软件设计人员应该注意的与编码相关的事项。本标准适用于嵌入式软件生存周期的编码阶段,主要供具有语言编程能力的软件编码人员使用。2 规范性引用文件下列文件对于本文件的应用是必不可少的。凡是注日期的引用文件,仅所注日期的版本适用于本文件。凡是不注日期的引用文件,其最新版本(包括所有的修改单)适用于本文件。GB/T 15272程序设计语言3 术语和定义GB/T 15272中界定的以及下列术语和定义适用于本文件。3.1单边作用 side effect指在表达式执行后对程序运行环境可能会造成影响。赋值语句、自增等操作都是典型的具有单边作用的操作。4 编码格式要求4.1 总体要求4.1.1 编程前应阅读硬件电路和芯片资料熟悉芯片的各种内存、寄存器地址、中断服务、定时器、通讯接口等功能,必要时将相关信息加入程序注释中。4.1.2 编程中应注意程序的存储空间如指令空间大小、数据空间大小、堆栈空间大小等是否超出系统有关限制。4.1.3 使用联合体时,应明确该编译器联合体存储的细节如联合体的末尾有多少个填充单位、联合体的各个成员如何对齐、多字节的数据类型高低字节如何排放顺序等。4.1.4 宜注意硬件系统复位和软件复位的方法和区别在程序中宜使用芯片提供的看门狗功能实现硬件系统复位。4.1.5 宜注意CPU对各种存储器的访问速度在程序中宜利用各种硬件设备自身的特点来提高程序效率。4.1.6 应注意所使用的编译器的位数,支持的C语言标准,调试程序所占用的内存,兼容性等特点。4.1.7 应注意编译器处理不同数据类型的原则及有关细节如static局部变量将在内存数据区中生成,而非static局部变量将在堆栈中生成。4.1.8 应注意程序开发调试环境和实际应用环境的区别。4.1.9 应用程序宜使用操作系统驱动程序来调用硬件端口。4.2 内存空间管理类4.2.1 在使用malloc等其它函数获取内存时,应判断申请内存是否成功。4.2.2 动态内存的申请与释放应配对,防止内存泄漏。应用场景主要包括:a) 过程/函数中分配的内存,在过程/函数退出之前要释放;b) 过程/函数中申请的(为打开文件而使用的)文件句柄,在过程/函数退出之前要关闭。错误用法示例:int example_fun( BYTE gt_len, BYTE *gt_code ) BYTE *gt_buf; gt_buf = (BYTE *) malloc (MAX_GT_LENGTH); . /* global title length error */ if (gt_len MAX_GT_LENGTH) return GT_LENGTH_ERROR; /退出之前没有释放gt_buf . / other program code 正确用法示例:int example_fun( BYTE gt_len, BYTE *gt_code ) BYTE *gt_buf; gt_buf = (BYTE * ) malloc ( MAX_GT_LENGTH ); . /* global title length error */ if (gt_len MAX_GT_LENGTH) free( gt_buf ); / 退出之前释放gt_buf return GT_LENGTH_ERROR; . / other program code4.2.3 不应引用已经释放的内存空间。4.2.4 应防止内存操作越界。应防止越界操作数组、指针、内存地址等内存空间。4.2.5 字符串连接宜使用strncat库函数代替strcat库函数,字符串拷贝宜使用strncpy库函数代替strcpy库函数,避免长度不够引起的数组越界。4.2.6 使用sprintf库函数时,应注意字符长度,避免长度不够引起的数组越界。4.2.7 对于内存受限的系统,宜少用动态内存分配,尽量选用数组。4.2.8 对于内存受限的系统,在分配内存时,应考虑内存碎片的问题。4.3 中断处理类4.3.1 中断服务程序不应有返回值。4.3.2 中断服务程序中不应使用printf()函数。4.3.3 对于中断中使用到的非局部变量,在中断处理函数中应对其进行入栈保护。4.3.4 中断处理程序中的变量,如果会被其他函数执行读操作或者写操作,那么在其他函数读写这个变量前,应先关中断,读写完,再开中断。4.3.5 对于开关中断,要注意成对匹配。对于默认开启的中断,如果在某个函数中进行了关闭,在函数退出时需进行相应的开启。对于默认关闭的中断,如果在某个函数中进行了开启,在函数退出时需进行相应的关闭。4.3.6 宜避免在中断服务程序中进行浮点数运算。4.4 系统接口类4.4.1 不应随意更改其它模块或系统的有关设置和配置。4.4.2 不应随意改变与其它模块的接口。4.4.3 应充分了解系统的接口之后,再使用系统提供的功能。4.5 硬件系统初始化类4.5.1 系统运行之初,应初始化有关变量及运行环境。4.5.2 系统运行之初,应对加载到系统中的数据进行一致性检查。4.5.3 在硬件系统初始化之前,宜判断工作电压是否已经稳定。4.6 软件模块初始化类4.6.1 所有变量在使用之前应被初始化。错误用法示例:unsigned int x; unsigned int y; y=x; /* x 没有初始值 */ 4.6.2 对局部声明不应用 extern 初始化。错误用法示例:int fn(void) extern int x = 0; return(x);4.6.3 数组、结构和联合的初始化列表应显式描述。数组、结构和联合的初始化列表应使用大括号,并使用附加的大括号来指示嵌套的结构;程序员应显式地考虑和描述复杂数据类型的所有元素,不应忽略某个元素的初始化。错误用法示例:int16_t y32 = 1, 2, 3, 4, 5, 6 ;int16_t y32 = 1, 2 ;正确用法示例:int16_t y32 = 1, 2 , 3, 4 , 5, 6 ;4.6.4 枚举元素的初始化应完整。枚举元素的初始化只有两种形式是安全的,一是初始化所有元素,二是只初始化第一个元素。错误用法示例:enum E_typenum1, num2 = 2, num3;4.7 版面书写类4.7.1 文件注释应采用统一格式。文件注释示例: /* * Copyright (c) 2007, 公司名称 * All rights reserved. * 文件名称: filename.c * 摘 要: 简要描述本文件的内容 * 当前版本: x.x, 编写者/修改者,修改时间,修改内容 * 历史版本: x.x, 编写者/修改者,修改时间,修改内容 */ 4.7.2 函数注释应采用统一格式。函数注释示例: /* * 函数介绍: * 参数: * 返回值: * 备注: */ 4.7.3 不应使用嵌套的注释。4.7.4 应保证注释与代码的一致性,无用的注释应删除。4.7.5 对单条语句代码的注释应放在其上方或右方相邻位置。4.7.6 对于有物理含义的变量、常量、数据结构(包括数组、结构、类和枚举等),如果其命名不是充分自注释的,在声明时应加以注释,说明其物理含义。4.7.7 控制语句应用大括号括起来。错误用法示例: if (a = 1) x = y; while (c = getchar( ) != EOF) putchar(c); for ( ; ; ) timetest(n);正确用法示例: if (a = 1) x = y; while (c = getchar( ) != EOF) putchar(c); for ( ; ; ) timetest(n);4.7.8 注释与其上面的代码应用空行隔开。4.7.9 宏定义应大写。正确用法示例:#define MAX 100 #define PI 3.14159 4.7.10 函数风格的宏应用括号“()”括起来。错误用法示例: #define ADD(a, b) a + b正确用法示例:#define ADD(a, b) (a) + (b)4.7.11 代码中的一行中只应有一个声明或者一条语句。错误用法示例: int i; int j; /*多个声明*/ j = i; i+; /*多条语句*/4.7.12 超过120个字符的长语句,应分成多行书写。4.7.13 一个文件中的程序总行不宜超过2000行。4.7.14 一个函数中的程序总行不宜超过200行。4.7.15 宜用括号“()”明确表达式的操作顺序,避免使用默认优先级。4.7.16 宜避免使用逗号操作符。4.8 声明定义类4.8.1 头文件中不应有对象或函数的定义。头文件中只存放“声明”而不存放“定义”。4.8.2 不应单独使用小写字母“l”或大写字母“O”作为变量名。小写字母“l”很容易与数字“1”混淆,大写字母“O”很容易与数字“0”混淆。4.8.3 函数参数不应只有类型名没有标识符。4.8.4 使用八进制数应加以注释。八进制数是以0开始,易与十进制数混淆。4.8.5 局部变量不应与全局变量重名。4.8.6 不应使用未知大小的数组。当声明一个数组时,其大小应该显式声明或者通过初始化进行隐式定义。4.8.7 不应重新定义使用C的关键字。4.8.8 non-void类型函数的所有出口路径都应该有一个明确的return语句表达式,而不应该只有return。错误用法示例:int func(void) return;4.8.9 在函数参数中不应使用static存储类标识符。错误用法示例:int func(static int x);4.8.10 类函数宏调用时不应没有它的参数,并且参数不应多于或少于宏定义时的参数。错误用法示例:# define ABC(x) ( (x) + 3 )b = ABC;#define MAX(a, b) (a) (b) ? (a) : (b)long maxnum = MAX(intnum);4.8.11 字符型变量应明确定义是有符号的还是无符号的。4.8.12 在同一嵌入式软件产品内,应规划好接口部分标识符(变量、结构、函数及常量)的命名,防止编译、链接时产生冲突。例如:可规定接口部分的变量与常量之前加上“模块”标识等。4.8.13 在文件范围内声明和定义的所有对象或函数应具有内部链接,除非是在需要外部链接的情况下。如果一个变量只是被同一文件中的函数所使用,那么就用static。类似地,如果一个函数只是在同一文件中的其他地方调用,那么就用static。使用static存储类标识符将确保标识符只是在声明它的文件中是可见的,并且避免了和其他文件或库中的相同标识符发生混淆的可能性。4.8.14 用typedef自定义的类型不应被重新定义。改变用户自定义的类型会引起混淆甚至能导致错误,因此用typedef自定义的类型不应被重新定义。4.8.15 预处理语句“#if”和“#endif”不应分散在不同的文件之中。4.8.16 被包含文件中的“#else”、“#elseif”、“#endif”不应与父文件中的“#if”匹配。4.8.17 “#if”表达式中使用的宏应该已经被定义,并且“#if”表达式只能包含整型常量。错误用法示例:#define BIGINT 32767#define BIGREAL 1e38#if (BIGREAL BIGINT) /* BIGREAL是浮点型常数 */.#endif4.8.18 应避免一个头文件的内容被包含两次。4.8.19 宏如果需要被重定义,应该先用“#undef”解除前面的定义。4.8.20 除了编译开关/头文件等特殊应用,不应使用“_EXAMPLE_TEST_”之类以下划线开始和结尾的定义。4.8.21 限定符static不应用于定义标记。错误用法示例:static struct ST1 int a; int b ;4.8.22 地址操作符“&”不应用于一个声明为“register”类型的对象。错误用法示例:static void foo (void)register int a;int *pa;pa = &a; /* “&”不应用于一个声明为“register”类型的对象 */register int array10;int * ptr = &array0; /* “&”不应用于一个声明为“register”类型的对象*/4.8.23 struct或union类型中至少应有一个成员。4.8.24 对struct或union成员不应使用存储类指示符。错误用法示例:struct record extern int field; 4.8.25 struct或union类型的成员不应是void类型、函数类型、未知大小的数组和含有未知内容的struct或union。4.8.26 宜使用“typedef”在头文件中对基本类型定义。正确用法示例: typedef char char_t; 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; typedef long double float128_t;4.8.27 对只设置一次的变量,宜使用“const”限定词进行声明。4.8.28 宜优先考虑使用函数而非函数式宏定义,在宏参数中不宜包含自增、自减或赋值等带有单边作用的操作符,宏参数中也不宜包含函数调用。4.8.29 函数中不宜使用过多的参数,建议不超过20个。4.8.30 不宜在一个函数块中单独使用“#define”或“#undef”。4.8.31 应谨慎使用“#pragma”。4.9 控制语句类4.9.1 条件判别成立时相应分支不应无执行语句。4.9.2 在ifelse if语句中应使用else分支。4.9.3 在switch语句中应有default语句。4.9.4 不应使用空switch语句。4.9.5 switch语句中不应只包含default语句。4.9.6 所有非空的switch case子句都应以break 语句结束。4.9.7 switch控制表达式不应是一个常量或逻辑表达式。逻辑表达式只有真和假两种情况,当需要对逻辑表达式判别时,应使用if else语句而不是switch语句。4.9.8 每一个switch语句都应至少有一个case子句。在switch的case或default之前不应存在声明和语句。4.9.9 case表达式的类型应与switch控制表达式的类型一致。4.9.10 switch的子句不应位于一个嵌套的代码块中。错误用法示例:int foo(int i, int p) int ret = p; switch (i) case 0: if (ret 2) ret+; break; case 1: ret = 2; break; case 2: ret = 3; break; default: ret = 4; break;4.9.11 case表达式的大小不应超过switch控制表达式的大小。4.9.12 循环控制变量的类型应为整型。浮点数不应用作循环控制变量,在循环执行时会得到不可预料的结果。 错误用法示例: for (ft = 0.0F; ft 10.0F; ft = ft + 2.0F) +loop; 4.9.13 循环控制变量应为局部变量。循环变量应在在最小的范围内定义,以免变量被外部修改导致循环不可控制,即循环变量的作用域应最小。4.9.14 for循环控制语句中的3 个表达式只应和循环控制相关。第一个表达式只能为循环变量赋初值,第二个表达式只能进行循环条件的判断,第三个表达式只能进行循环变量增(减) 值这样可以增强程序的可理解性。错误用法示例:for (i = 0; i 10; i+) /*第一个表达式不是赋值表达式*/4.9.15 for 循环中,循环变量只应在for循环控制语句的第三个表达式中修改,不应在循环体中修改。4.9.16 不应直接从函数中跳出。直接从函数中跳出破坏了程序的结构化。错误用法示例:#include void foo(jmp_buf mark, unsigned int val)longjmp(mark, val);4.9.17 不应使用 goto 语句及标号。4.9.18 不宜在循环中使用break语句。4.9.19 for循环不宜只执行一次。4.10 类型转换类4.10.1 应减少没有必要的数据类型默认转换与强制转换。对编译系统默认的数据类型转换,应有充分的认识,默认的数据类型转换有可能损失数据的精度,可能改变数据的符号属性。表达式如果是函数的参数或函数的返回表达式,不能出现默认数据类型转换。当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。常数被默认转换为无符号数时,应充分考虑到无符号变量的空间大小。4.10.2 应禁止 signed 类型与 unsigned 类型之间的隐式转化。从有符号类型转换为无符号类型会导致符号的丢失。无符号数转换为有符号数,可能得到一个负值。4.10.3 应禁止 int 类型与 float 类型的隐式转化。浮点类型与整型之间的相互转换为会导致精度的丢失。错误用法示例: double d64_a; float f32_a; signed int i16_a; . d64_a = i16_a; /* 整型数转换为浮点数 */ si16_a = f32_a; /* 浮点数转换为整型数 */ . 4.10.4 不应对指针变量使用强制类型转换赋值。错误用法示例:void foo(void)unsigned short s = 0;unsigned int *p1_ptr;p1_prt = (unsigned int*)s; 4.11 指针、数组使用类4.11.1 不应把自动类型的局部变量的地址赋值给外部指针。局部变量的地址赋值给外部或者范围更大的指针,如果局部变量不是静态的,那么这种用法是不安全的。错误用法示例: extern int* pi; void f(void) int a; pi = &a; 4.11.2 指针的指针不应超过两级。4.11.3 指向不同数据类型的指针之间不应相互转换。不同的数据类型分配的字节空间可能不同,将指向多字节空间的指针转换为指向少字节空间的指针不会有问题,反之有可能出现错误。4.11.4 指针转换过程中不应丢失指针的const、 volatile属性。示例:int x;int *pi; /* 指向整形的指针 */int *const pci1 = &x; /* const指针 */const int *pci2; /* 指向const整形的指针 */volatile int *pci3; /* 指向volatile整形的指针 */.pi = pci1; /* 符合本条要求 */pi = (int *)pci2; /* 不符合本条要求 */pi = (int *)pci3; /* 不符合本条要求 */4.11.5 只有指向数组的指针才允许进行算术运算。注: 此处的算术运算仅仅限定于指针加减某个整数,比如ppoint = point -5, ppoint+等。错误用法示例: int *px; int i; px = &i; px = px + 2; +px;正确用法示例:int *px;int a10;px = a;px = px + 2;+px;4.11.6 只有指向同一数组的两个指针才允许相减。注: 此处两个指针可以指向同一数组的不同成员。错误用法示例:int a10;int b10;int x, *p1, *p2;x = p1 p2;4.11.7 只有指向同一数组的两个指针才允许用、=、和=等关系运算符进行比较。注: 此处两个指针可以指向同一数组的不同成员。错误用法示例:char a10;char b10;int x;x = a b;正确用法示例:char a10;char *p1, *p2;int x;p1 = a;p2 = a+2;x = p1 p2;4.11.8 指针的索引值不应为负。错误用法示例:void foo(int *p, int n) int r; if (n -5) r = pn; /* 指针的索引值为负 */ . r = *(p + n); /* 指针的索引值为负 */ 4.11.9 不应对指针值可能为NULL的指针进行算术运算。错误用法示例:extern int buf10;void foo( int n) int *p = NULL; for (i = 1; i = n; +i) . p = &bufi; . +p; /* 对指针值可能为NULL的指针进行算术运算 */4.11.10 对传递到函数的指针参数应进行是否为空判断。4.11.11 调用返回类型为指针的函数后,应进行是否为空的判断。4.11.12 除常量字符串外,其他字符数组应指定长度。4.11.13 局部变量的地址不应在本对象消亡后传给另外一个对象。本条要求分为以下几种情况:a) 将动态分配的局部变量的地址赋值给一个带链接的或更宽范围的指针是危险的,一旦局部变量的内存空间被释放,这个指针会成为无效的指针(见示例1);b) 函数参数不应返回动态数据的地址(见示例2);c) 函数参数不应返回本地静态数据的地址(见示例3)。错误用法示例:示例1:extern int* pi;void f(void) int a; pi = &a;示例2:void foo(int *appi) int bi = 1; . *appi = &bi; return;示例3:例3:void foo(int *appi) static int bi = 1; . *appi = &bi; return;4.11.14 一个未知大小的对象的指针不应成为加法或者减法操作的左操作数。错误用法示例:typedef struct TAG T;static int foo(int n, void *pv) T *v1 = 0; v1 = v1 + n; /* 不符合本条要求 */ pv = pv + n; /* 不符合本条要求 */ v1 = v1 - n; /* 不符合本条要求*/ pv = pv - n; /* 不符合本条要求 */4.11.15 数组的索引值不应越界。4.11.16 宜减少指针的使用。4.11.17 多线程应用中被几个任务共享的变量宜使用“volatile”。4.12 运算处理类 4.12.1 不应对有符号数进行位运算。4.12.2 无格式的字符型不应与负数常量和零比较。注: 无格式的字符型指既没有定义为unsigned也没有定义为signed的char类型。不同的编译器对其处理是不一样的,有的定义其可正可负,有的定义其只能为正。错误用法示例:void f(char c1)if (c1 -3) /* 和负数常量比较 */ c1+; if (c1 B-C-A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。4.14 数据冗余类4.14.1 不应存在执行不到的代码。4.14.2 表达式语句上层不应包括多余的操作符和转换。错误用法示例:a = b+; /* 该表达式包含的操作符“=”没有被用到 */(int)(a += 1);4.15 程序效率类4.15.
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025北京市规划和自然资源委员会事业单位招聘55人考前自测高频考点模拟试题及一套参考答案详解
- 2025年哈尔滨市急救中心编制外合同制工作人员招聘5人模拟试卷完整参考答案详解
- 2025江西南昌动物园百花园管理所招聘3人模拟试卷及一套完整答案详解
- 2025福建福州市罗源县卫健系统事业单位招聘编内卫技人员41人模拟试卷及一套完整答案详解
- 2025中核集团中国核建春季校园招聘笔试题库历年考点版附带答案详解
- 2025中国建筑一局(集团)有限公司财务管理部合并财务报表管理岗招聘1人笔试题库历年考点版附带答案详解
- 美团安全知识培训内容课件
- 2025年初级会计《经济法基础》知识点:合同法的基本原则
- 2025医疗设备采购协议范本
- 2025现代化生产基地食堂外包协议
- 2025保密观知识竞赛题库(试题附答案25个)
- 2025-2026学年人教版(2024)初中生物八年级上册(全册)教学设计(附目录)
- 煤矿监管培训方案
- 企业反腐倡廉培训课件
- 湿疮湿疹中医护理查房
- 2025年6月新《中华人民共和国治安管理处罚法》全文+修订宣贯解读课件(原创内容丰富且全)
- DB31/T 1377.4-2022实验鸡和鸭第4部分:设施及环境
- 2025邮储银行面试题目及答案
- 他人借车免责协议书
- 城中村改造项目规划设计(仅供参考)
- 公司代经营合同范例
评论
0/150
提交评论