文档库 最新最全的文档下载
当前位置:文档库 › C语言编程规范

C语言编程规范

C语言编程规范

1范围 (3)

2规范性引用文件 (3)

3术语和缩写 (3)

3.1 标识符(IDENTIFIER) (3)

3.2定义(DEFINE) (3)

3.3声明(DECLARE) (3)

3.4宏(MACRO): (3)

3.5固有数据类型(或预定义或基本数据类型) (4)

3.6用户定义数据类型 (4)

3.7范围 (4)

3.8全局 (4)

3.9文件级局部 (4)

3.10函数级局部 (4)

3.11头文件 (4)

3.12源文件 (4)

4命名规则(naming conventions) (4)

5文件组织(file organization) (5)

5.1头文件的结构 (5)

5.2源文件的结构 (6)

5.3目录结构 (6)

5.4其它 (6)

6程序板式 (7)

6.1空行 (7)

6.2代码行 (7)

6.3代码行内的空格 (7)

6.4对齐 (8)

6.5长行拆分 (8)

6.6修饰符的位置 (8)

6.7注释 (8)

7编程规则和建议 (9)

7.1表达式 (9)

7.2声明和定义 (10)

7.3整数数据类型及操作 (11)

7.4字符操作 (11)

7.5浮点型 (11)

7.6类型转换 (11)

7.7常量 (12)

7.8指针 (12)

7.9数组 (12)

7.10函数与宏 (12)

7.11控制语句 (13)

7.12内存分配 (14)

7.13错误处理 (14)

7.14断言 (14)

7.15通用规则和建议 (14)

8附件 (15)

8.1头文件模版 (15)

8.2源文件模版 (15)

8.3函数头模版 (15)

在软件编码过程中,良好的程序书写风格, 可以使程序清晰易懂, 层次分明, 易于理解与维护。特别是在项目组内,许多程序员共同开发同一软件系统时,这一点显得尤为重要;采用统一的编程风格和命名体系,有利于各个项目组的软件人员交流、流通和相同软件模块的移植,对彼此交流和协同开发将起到事半功倍的作用。

1.1范围

本文档的目的是为企业使用C语言进行软件开发提供一个使用规范指南,包括:

-命名规则

-文件的组织

-程序风格样式

-编程规则和建议

本编程规范中的规则必须遵守,而建议则希望被遵守。

规则0:如有特殊原因违背规则时,必须有相应的文档记录和审核记录,第三方软件不受本编程规范约束。

1.2规范性引用文件

1.3术语和缩写

1.4标识符(identifier)

指C中的一个变量、常量、函数或类型。在本文档中它与“名称”同义。

1.5定义(define)

变量或常量的定义是指编译器为其分配的内存。函数的定义是指函数体的实现。

1.6声明(declare)

指一些实体对编译器的声明所在的位置,如函数原型、类型(由typedef定义)、变量和常量。

1.7宏(macro):

指由#define语句定义的代替一个文本串的名字。当该名字在源程序中出现时,预处理器用对应文本串代替它。

1.8固有数据类型(或预定义或基本数据类型)

由语言本身定义的数据类型,如int。

1.9用户定义数据类型

编程人员用struct、union、enum或typedef定义的数据类型。

1.10范围

标识符的范围是指其可见的环境。环境在这里指可以使用该标识符的函数或程序块。

1.11全局

使用范围没有限制。

1.12文件级局部

使用范围局限在一个文件中。

1.13函数级局部

使用范围局限在一个函数中。

1.14头文件

C程序中用于保存程序的声明的文件称为头文件;头文件以” .h ”为后缀。

1.15源文件

C程序中用于保存程序的实现的文件称为源文件,源文件以” .c ”为后缀。

1.16 命名规则(naming conventions)

命名规则见表1。

1.17 文件组织(file organization)

1.18头文件的结构

规则5.1.1:头文件中的不同类型声明按照以下顺序排列:版权声明、公司名称(英文);

文件注释(包括文件名称、功能、编写日期、说明和修改历史);

头文件保护开头(#ifndef 文件名_H #define 文件名_H);

包含文件声明;

宏和常量定义;

类型定义(typedef);

全局变量声明(extern);

外部函数原型声明(extern);

头文件保护结尾(#endif);

头文件结束(即在头文件尾加一回车);

头文件模板具体见“头文件模板”。

1.19源文件的结构

●规则5.2.1:源文件按照以下顺序组织:

版权声明、公司名称(英文);

文件注释(包括文件名称、功能、版本、编写日期、说明和修改历史);

包含文件声明;

局部宏定义;

局部常数和类型定义;

全局变量定义/初始化;

局部函数原型声明;

函数实现;

源文件结束;

源文件的模板具体见“源文件模板”。

1.20目录结构

●规则5.3.1:头文件和源文件分别保存于不同的目录。

●建议5.3.1:私有的头文件可以直接在源文件中声明。

1.21其它

●规则5.4.1:要隔离出依赖于机器硬件的代码(DSP为提高效率,采用处理器提供的专用

指令除外)。

●规则5.4.2:将组成某个软件包的一个或多个源文件的接口声明单独放在一个头文件中

●规则5.4.3:在#include指示中不能明确指定操作系统、或文件绝对路径、或相对于当前工

作目录的文件路径。用#include <文件名.扩展名>代替#include “文件名.扩展名”;符号<>指示预处理程序到预定义的缺省目录下寻找文件,该路径通常在INCLUDE环境变量中指

定的。符号“”指示预处理程序先到当前目录下寻找文件,再到预定义的缺省路径下寻

找文件。

1.22 程序板式

版式虽然不会影响程序的功能,但会影响可读性。程序的版式追求清晰、美观,是程序风格的重要构成因素。

1.23空行

空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。

●建议6.1.1:在相对独立的程序块之间、变量说明之后、每个函数定义结束之后都要加一行

空行。

●建议6.1.2:在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分

隔。

1.24代码行

●规则6.2.1:每行最多包含一个声明。

●规则6.2.2:if、for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句

有多少都要加”{}”。

●建议6.2.1:变量的声明与初始化应该尽可能同时进行。

●建议6.2.2:用统一的格式书写预处理指令。

1.25代码行内的空格

●规则6.3.1:赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=“、”+=“、

“>=“、”<=“、”+”、” * ”、”%”、”&&”、” || “、”<<“,”^”等二元操作符的前后必须加空格。

但是,如果将二元操作符以及前后两个操作数作为一个整体使用了括号时,则在该操作符

前后可以适当减少空格,以使代码更紧凑,阅读起来更清晰。

●规则6.3.2:一元操作符”! ”、”~”、”++”、”--” ”&(地址运算符) ”等与操作数之间不

得加空格。

●规则6.3.3:在”[]” 与数组名之间及”.”、”->“操作符的前后不得加空格。

●建议6.3.1:函数名之后不要留空格,紧跟左括号”(”。

●建议6.3.2:”(”向后紧跟;”)”、”,”、”;”向前紧跟,紧跟处不留空格。

●建议6.3.3:”,”之后要留空格。如果”;”不是一行的结束符号,其后要留空格。

●建议6.3.4:关键字之后要留空格。const、case 等关键字之后至少要留一个空格;if、for、

while等关键字之后应留一个空格再跟左括号”(”。

●建议6.3.5:三元操作符周围要有空格。

1.26对齐

●规则 6.4.1:程序的分界符”{“和”}”应独占一行并且位于同一列,同时要和本代码

块”{“外的语句首字母对齐。

●规则6.4.2:{ } 之内的代码块在”{“右边4个空格处左对齐。

●规则6.4.3:程序左边界以4空格的倍数对齐,禁止使用Tab键。

1.27长行拆分

●规则6.5.1:代码行一行不能超过200个字符。

●规则6.5.2:长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出

操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。

●建议6.5.1 尽可能使函数和模块的功能单一化,避免使用超大的万能函数。在一个函数中,

语句数不要超过200,在一个模块中,语句数不要超过2000。

1.28修饰符的位置

●规则6.6.1:必须将修饰符* 和&紧靠变量名。

1.29注释

C语言的注释符为”/*…*/”。注释通常用于:

(1)版本、版权声明;

(2)函数接口说明;

(3)重要的代码行或段落提示。

●规则6.7.1:注释符为” /*……*/”,注释量要达到代码量的30%,同时注意注释的有效性。

注释量的计算按如下方法:

注:如果一行中既有代码又有注释,则既算一行代码,也算一行注释。

●规则6.7.2:注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下

方。

●规则6.7.3:必须在每个函数定义前加一个恰当的函数说明(函数头),描述该函数的功能、

参数、返回值、说明和修改记录等。

函数头的模版具体见“函数头模板”

●规则6.7.4:边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。

不再有用的注释要删除。

●规则6.7.5:数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,

必须给每个域(或元素)加以注释。

●规则6.7.6:全局变量要有较详细的注释,包括对其功能、取值范围、所属子系统或模块、

以及存取时的注意事项等说明。

●规则6.7.7:注释与所描述内容进行同样的缩排。

●建议6.7.1:在所有代码块(如各类分支、判断、循环等)的起始位置处进行注释,说明该

代码块的功能、意图、结束(判断)条件等,如果本代码块超过30行,则需要在代码块的

“}”结束处右方加注释“end of xxxx”,以表明该代码块结束。

●建议6.7.2:尽量避免在注释中使用缩写,特别是不常用缩写。

●建议6.7.3:尽量通过对函数或过程、变量、结构等正确的命名以及合理地组织代码的结构,

使代码成为自注释的。

●建议6.7.4:注释应尽量考虑程序易读及外观排版的因素,使用的语言若是中、英兼有的,

建议多使用中文,除非能用非常流利准确的英文表达

1.30编程规则和建议

1.31表达式

●规则7.1.1:在表达式中用括号使子表达式的运算顺序清晰;必须确保表达式中的操作数的

运算顺序不会对表达式的值造成影响,尤其在可能有副作用的子表达式中,必须保证有准确定义的运算顺序。

●规则7.1.2:在一条语句中只能对某一变量/对象的值做一次修改。

反例:一条语句中对同一变量的多次修改

y = (x++)* (x++);

●规则7.1.3:不要在同一条I/O输入语句中用刚赋值的变量再做参数调用。

●规则7.1.4:在写if 语句的条件时,如果是变量和常量的比较关系,将常量放在前面。

●建议7.1.1:不要在逻辑表达式中使用按位操作符”&”和”|”。

●建议7.1.2:表达式尽可能清晰简明;避免表达式中的副作用。

●建议7.1.3:在if,for和while语句中要使用布尔型控制表达式。如果该表达式不是逻辑表

达式,就用显式验证。

反例:表达式隐式验证

while (a + b)

范例:表达式显式验证

while ((a + b) != 0)

●建议7.1.4:尽量避免表达式中混有带符号数和无符号数。当带符号数和无符号数混和运算

时,有符号数有可能转变为无符号数。如果有符号数是个负数,转换后可能变为正数。这可能导致不可预料的结果。

●建议7.1.5:避免逻辑表达式的结果为恒真或恒假。这样的判断往往是毫无意义的,有时也

是潜在隐藏的bug,不能到达程序的某些部分。

1.32声明和定义

●规则7.2.1:全局常量、变量或函数的定义和所有声明要始终使用一致的类型,切不可在局

部来声明和定义它们。

在一个所有使用该类型的源程序都包含的头文件中声明全局常量、变量或函数,可以避免这个问题。

●规则7.2.2:在整个子系统中唯一地定义源文件级局部类型。

●规则7.2.3:要使用在项目级定义的类型别名,不要用固有数据类型(enum例外),这样代

码不依赖于特定的编译环境,可移植性较好。

●规则7.2.4:永远将extern,static和register写在具有这些属性的声明或定义的行首。

●规则7.2.5:要明确地指明函数返回值的类型。

●建议7.2.1:函数内部使用的变量,其声明和定义放在函数的开始处。

●建议7.2.2:在变量的可见空间内不要定义重名变量。

●建议5.2.3:在最小可能的范围内声明变量。

1.33整数数据类型及操作

C语言中提供了三种“整型”:

字符型:char,又可加上修饰符signed,unsigned;

int 型:包括short,long两种,又可加上修饰符signed,unsigned;

枚举型:enum。

C中没有严格的类型检查,比如它允许在表达式和变量中混用整型,此时编译器自动完成转换(隐式转换),结果依赖于特定的工具,这样的数据操作通常是危险的。

●规则7.3.1:只能对无符号整数进行移位操作;(对于DSP相关算法的代码,在预知结果的

情况下,可以对有符号数进行移位操作)。ANSI C没有对负数右移位的结果作规定。而负数左移位会改变其符号和值,且改变的结果不可预知。

●规则7.3.2:移动的位数只能大于等于0并小于等于被移变量的长度。

●建议7.3.1:在除”/”或”%”取模操作中,用显式检查避免被0除或模0。如果操作数符

号不同,使用标准库函数div和ldiv代替除/取模操作。

●建议7.3.2:在程序的关键部分加充分的检查,以防止并保护上溢和下溢。

●建议7.3.3:不要用数字直接表示结构的偏移量,要用宏offsetof。

●建议7.3.4:对于不应有负值的变量用无符号整型(unsigned)定义。

●建议7.3.5:只能将enum型变量与同类型变量相比较。虽然不同enum类型的变量相比较

不会出现语法错误,但是会影响程序的可读性和逻辑性。

1.34字符操作

●建议7.4.1:不要对字符集的顺序做任何假设。

●建议7.4.2:显式定义带符号或无符号字符。

●建议7.4.3:如果用8位ASCII码,将char设置为无符号。

1.35浮点型

C提供浮点类型float, double和long double,其表示法在标准头文件中定义。

●规则7.5.1:要恰当地进行两个浮点数之间的比较,不要直接使用”==”或”!=”比较。

1.36类型转换

●类型转换分为隐式类型转换和显式类型转换。

●规则7.6.1:对于不同类型,必须强制转换。

●规则7.6.2:不能对const定义的常量进行类型转换(对某些处理器从优化效率方面考虑有

特殊要求的除外)。

●建议7.6.1:尽量不做指针和非指针类型之间的转换。

●建议7.6.2:切不可将指向函数的指针转换为指向对象或其他任何类型的指针,反之亦不可。

●建议7.6.3:强制类型转换以后,涉及到数据类型的数位变化时,要用”&”操作对结果进行

保护。例如将int型强制转换为char型时,建议做&0xff操作进行保护。

1.37常量

●规则7.7.1:代码中不要直接使用具有某种意义的数字,而要用符号值代替。不希望被改变

的数字必须定义为常量或宏,这一规则也适用于字符串。

●建议7.7.1:对于所有具有物理含义的变量、常量、宏,在命名时尽量使其名称是充分自注

释的;如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义

●建议7.7.2:利用语言去计算对象的大小而不要使用常数。

1.38指针

●规则7.8.1:不要对指针进行”!”,”&&”,或”||”操作,不要将两个指针相加;只能对指向相

同数组的指针进行比较或相减。

两个指针相减,仅在两个指针指向同一数组中的元素时有效。指针和整型数n相加,结果

为一个指针,该指针与原指针之间相距n个元素。

●规则7.8.2:不要将指针和NULL相比较或给指针分配NULL值,而用NULLPTR代替。

因为NULL空值在不同编译环境下具有不同的含义。

●建议7.8.1:避免用指针指向指针。

1.39数组

●规则7.9.1:不要将数组声明为函数的参数,用指针符号。

●建议7.9.1:对多维数组的操作尽量用下标符号。

1.40函数与宏

●规则7.10.1:在有非空结果的函数的所有结束处写返回语句。

●规则7.10.2:如果在宏定义中使用了表达式,则一定要用括号把宏定义中表达式括起来。

●规则7.10.3:在函数原型声明中确定参数的名字,在函数定义中使用相同的名字。

●建议7.10.1:避免函数带过多参数,超过7个参数即认为过多。

●建议7.10.2:不要在用户自定义的函数中使用变参数列表。

1.41控制语句

●建议7.11.1:尽量避免使用goto 语句,特别是局部的goto 语句。

●建议7.11.2:使用switch语句的建议:

●- 条件不是彼此互斥时,不要用switch语句;

●- 为switch表达式和case标签选择匹配的类型;

●- 在switch语句中,所有的case标签要采用同一个enum类型;即:如果case标签是enum

类型的话,不能用不同的enum类型。

●- 在switch语句的末尾写一条非空的default语句;

●- 不能在switch和第一个case之间放置语句;

●- 在每个case语句后,书写相应的注释;

●- 每个case标签要以break语句结束;

●- 每个case执行的语句用“{”“}”括起来;

●- 对于switch语句下的case语句,如果因为特殊情况需要处理完一个case后进入下一个

case处理,必须在该case语句处理完、下一个case语句前加上明确的注释;

●- case语句要求缩进。

●建议7.11.3:else-if语句的写法:

●else-if语句应该写成条件从上向下读,遇到第一个能够满足的条件,就执行所对应的语句

部分,而随后的结构都跳过去,最后一个else处理默认情况(即没有选中其他部分时的情况)或者给出一个错误信息。在书写的时候所有的else垂直对齐,而不是让每个else与对应的if对齐,这样的写法能强调所有的判断都是顺序进行的。

●建议7.11.4:循环语句的写法:

●循环语句尽量用for语句编写,并尽可能使用for(i = 0; i <= n; i++)的格式,保证循环变量的

增量操作写在for语句括号中第二个分号后。

●建议7.11.5:建议采用break,而不是标志判断跳出循环。

●建议7.11.6:避免使用逗号操作符。

●建议7.11.7:避免使用条件运算符。

●建议7.11.8:避免使用三元操作符,以条件语句取代。

1.42内存分配

●规则7.12.1:使用完所分配的内存后必须立即释放。

●建议7.12.1:不要用同一个指针既指向数组又指向单个的对象。

●建议7.12.2:当指针指向的内存被释放后,应给该指针赋一个空值(NULLPTR)或者赋一

个准备使用的新值。

●建议7.12.3:在堆栈(stack)而不是在堆(heap)中分配局部对象。

1.43错误处理

●建议7.13.1:如果被调用的函数有返回值,要针对不同的返回值进行相应的判断并作相应

的处理;如果返回值可能正确也可能错误,一定要对其进行差错检查。但是如果返回值可以不处理或不需要马上处理时,则要在此处添加说明。

1.44断言

断言函数可以用来明确地表达先决条件和后置条件,也可以用来记录和调试。断言使用逻辑表达式(布尔型)作为参数,能够在程序中的任意位置调用。如果表达式的值为假,断言函数会使程序非正常终止。如果表达式的值为真,调用断言函数不会产生任何影响。

●建议7.14.1:在下列情况中,应调用错误处理器,而尽量不要使用断言:

-某一设备返回错误代码;

-内存分配失败;

-函数的返回值指示某一错误;

-来自某一设备或外部子系统的数据表示错误或不一致。

1.45通用规则和建议

●建议7.15.1:编译时将告警级别设置为最高并消除所有警告;对不能消除的警告列出告警

号。

●建议7.15.2:除条件编译外尽量不要使用预处理器。

在C中用预处理器是危险的:

由宏控制引起的语法错误很难隔离出来

宏没有范围定义

宏会增加检查副作用的难度

宏有特殊的语法

宏不能用调试符号

但值得注意的是,对于一些特殊应用,没有方便的替代预处理器的方式(如用于条件编译的#if…,#ifdef…#endif结构、命名计划或命名拼接)

建议7.15.3:使文件之间在编译、连接时相互依赖性最小。

1.46附件

1.47头文件模版

1.48源文件模版

1.49函数头模版

相关文档