




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
天津通卡嵌入式C语言编码规范V1.000硬件开发中心文档历史记录版本号定义规则:使用阿拉伯数字,并由小数点分割成两部分。第一部分(1位有效数字):整体修改时使用。第二部分(3位有效数字):部分调整时使用。日期姓名版本更新记录2016-03-10李响V0.001草稿。参照北大青鸟、华为编程规范整理。2016-03-24李响V1.000按部门讨论结果整理修改。C语言编码规范目录1 文件结构 11.1 版权和版本的声明 11.2 头文件的结构 11.3 定义文件的结构 21.4 头文件和定义文件使用 32 程序版式 32.1 空行 32.2 代码行 42.3 代码行内的空格 52.4 对齐 52.5 长行拆分 62.6 修饰符的位置 72.7 注释 73 标识符命名 84 常量 94.1 const与#define的比较 94.2 常量定义 105 变量 106 结构体 117 表达式和基本语句 137.1 运算符的优先级 137.2 复合表达式 137.3 if语句布尔表达式 137.4 循环语句 157.5 switch语句 167.6 goto语句 168 函数设计 168.1 注释规则 168.2 函数的使用 168.3 函数名 168.4 参数的规则 168.5 返回值的规则 188.6 函数内部实现的规则 188.7 其它建议 199 内存管理 1910 可测性 1911 程序效率 1912 编译问题 2013 兼容性问题 2014 代码审查 2015 代码测试、维护 20附录A:通卡标准类型头文件及变量定义要求 22天津通卡硬件开发中心 C语言编码规范第18页共24页本规范适用于通卡公司硬件产品中自有嵌入式软件代码的编写。对于第三方提供的代码(如:驱动、系统文件、标准库等)可按其自有编码规范进行编写。编程过程中尽量将第三方提供代码整理到单独的文件中,与自有代码进行区分。文件结构C程序文件通常分为两类文件:一类文件用于保存程序的声明(declaration),称为头文件。头文件以“.h”为后缀。另一类文件用于保存程序的实现(implementation),称为定义(definition)文件。定义文件以“.c”为后缀。对于简单的C语言程序,一般在把头文件和程序定义文件放在一起,只有一个.c定义文件即可。而对于复杂的程序,则多采用头文件包含的形式并通过多个定义文件实现。版权和版本的声明 版权和版本的声明一般应该位于头文件和定义文件的开头(参见示例1-1),主要内容包括:版权信息;文件名称、文件标识、摘要;作者/修改者、修改日期、修改描述等;/************************************Copyright(c)2016,天津通卡*Allrightsreserved.*文件名称:filename.h*文件标识:根据软件工程设置*摘要:简要描述本文件的作用和内容等*************************************//*************************************作者:修改者名字,修改日期:20XX年4月5日*修改简要说明:**************************************/示例1-1版权和版本的声明头文件的结构头文件由三部分内容组成:文件开头处的版权和版本声明(参见示例1-1);预处理块;声明函数原型和声明数据结构或变量等。假设头文件名称为filename.h,头文件的结构参见示例1-2。/**************************************Copyright(c)2016,天津通卡*Allrightsreserved.**文件名称:filename.h*文件标识:根据软件工程设置*摘要:简要描述本文件的作用和内容等****************************************/#include<stdio.h>#include<stdlib.h>#include<graphics.h>//引用头文件#ifndefFILENAME_H//防止filename.h被重复引用#defineFILENAME_Hstructstudentstruct{intno;charname[20];charsex;floatscore;};voidGetValue();voidSetValue(intno);#endif示例1-2定义文件的结构定义文件有三部分内容:定义文件开头处的版权和版本声明(参见示例1-1);对一些头文件的引用;程序的实现体(包括数据和代码)。假设定义文件的名称为filename.c,定义文件的结构参见示例1-3/*版权和版本声明见示例1-1,此处省略。*/#inlcude“filename.h”/*函数的实现体*/voidGetValue(){…}/*函数的实现体*/voidSetValue(intno){…}voidmain(){…}示例1-3头文件和定义文件使用【规则1-1-1】在复杂的工程文件中,为了防止头文件被重复引用,应使用ifndef/define/endif结构产生预处理块。【规则1-1-2】用#include<filename.h>格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。【规则1-1-3】用#include“filename.h”格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。【规则1-1-4】不要在头文件中定义常量或变量,注意头文件只是用来声明。【规则1-1-5】将非系统的函数库放在一个单独的目录下引用。【规则1-1-6】头文件应按功能组织在一起,即对单独子系统的声明应放在单独的头文件中。此外,当代码从一个平台移植到另一个平台时有可能发生更改的声明应位于单独的头文件中,并进行相应的注释。【规则1-1-7】避免使用与函数库中专用头文件名相同的头文件名。语句#include"math.h"如果在当前目录中找不到所期望文件的话,会包括标准库math头文件。【规则1-1-8】包含头文件时一般不使用绝对路径名。程序版式空行空行起着分隔程序段落的作用,空行得体将使程序的布局更加清晰。空行不会浪费内存,所以不要舍不得用空行。【规则2-1-1】在函数内部局部变量定义结束之后处理语句之前要加空行。【规则2-1-2】在每个函数定义结束之后都要加空行。参见示例2-1(a)。【规则2-1-3】函数返回语句和其他语句之间使用空行分开。【规则2-1-4】在一个函数体内,逻辑上密切相关的语句之间不加空行,其它地方应加空行分隔。参见示例2-1(b)。//空行//空行while(condition){statement1;//空行if(condition){statement2;}else{statement3;}//空行statement4;}//空行voidFunction1(){…}//空行voidFunction2(){…}//空行voidFunction3(){…}示例2-1(a)函数之间的空行示例2-1(b)函数内部的空行代码行【规则2-2-1】一行代码只做一件事情,如只定义一个变量,或只写一条语句。不允许把多个短语句写在一行中。这样的代码容易阅读,并且方便写注释。【规则2-2-2】if、for、while、do、case、switch、default等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}表明是一个语句块。【规则2-2-3】一对花括号要单独各占一行。但是在do-while、struct和union及其后有‘;’的除外,要同在一行。例如:do{…}while(i>0);【规则2-2-4】switch语句中的每个case语句各占一行,当某个case语句不需要break语句最好加注释声明。【规则2-2-5】尽可能在定义变量的同时初始化该变量(就近原则),如果变量的引用处和其定义处相隔较远,变量的初始化很容易被忘记。如果引用了未被初始化的变量,可能会导致程序错误。本建议可以减少隐患。例如:intwidth=20;/*定义并初绐化width*/intheight=20;/*定义并初绐化height*/intdepth=20;/*定义并初绐化depth*/风格良好的代码行风格不良的代码行intwidth;/*宽度*/intheight;/*高度*/intdepth;/*深度*/intwidth,height,depth;/*宽度高度深度*/x=a+b;y=c+d;z=e+f;x=a+b;y=c+d;z=e+f;if(width<height){dosomething();}if(width<height)dosomething();for(initialization;condition;update){dosomething();}//空行other();for(initialization;condition;update)dosomething();other();代码行内的空格【规则2-3-1】关键字之后要留空格。象const、case等关键字之后至少要留一个空格,否则无法辨析关键字。象if、for、while等关键字和紧跟的左括号‘(’之后应留一个空格,右括号前也对应要留一个空格,以突出关键字。例如:if(a==b)【规则2-3-2】函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别。例如:voidcalc(void);【规则2-3-3】“,”之后要留空格,如Function(x,y,z)。如果“;”不是一行的结束符号,其后要留空格,如for(initialization;condition;update)。【规则2-3-4】不要在单目运算符(如“!”、“~”、“++”、“--”、“&”)和其操作对象间加空格。 例如:!foo,++i,(long)getValue【规则2-3-5】赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+=”、“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。【规则2-3-6】象“[]”、“.”、“->”这类操作符前后不加空格。例如:big.bar,pFile->bar,big[bar]【规则2-3-7】对于表达式较长的for语句和if语句,为了紧凑起见可以适当地去掉一些空格. 例如:for(i=0;i<10;i++)和if((a<=b)&&(c<=d))风格良好的空格风格不良空格voidFunc1(intx,inty,intz);voidFunc1(intx,inty,intz);if(year>=2000)if((a>=b)&&(c<=d))if(year>=2000)if(a>=b&&c<=d)for(i=0;i<10;i++)for(i=0;i<10;i++)for(i=0;i<10;i++)int*x=&y;int*x=&y;array[5]=0;a.Function();b->Function();array[5]=0;Function();b->Function();对齐【规则2-4-1】程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以及if、for、do、while、switch、case语句中的程序都要采用如上的缩进方式。【规则2-4-2】程序块要采用缩进风格编写,缩进的空格数为4个(定义一个tab键为四个空格)。【规则2-4-3】同属于一个语句块的代码对齐。【规则2-4-4】{}之内的代码块在‘{’右边一个tab键处左对齐。【规则2-4-5】函数或过程的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格,case语句下的情况处理语句也要遵从语句缩进要求。风格良好的对齐风格不良的对齐voidFunction(intx){programcode}voidFunction(intx){programcode}if(condition){programcode}else{programcode}if(condition){programcode}else{programcode}for(initialization;condition;update){programcode}for(initialization;condition;update){…programcode}while(condition){programcode}while(condition){…programcode}如果出现嵌套的{},则使用缩进对齐,如:{{}}长行拆分【规则2-5-1】代码行最大长度宜控制在70至80个字符以内。代码行不宜过长,否则不便于阅读,也不便于打印。【规则2-5-2】长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。【规则2-5-3】若函数或过程中的参数较长,则要进行适当的划分。if((very_longer_variable1>=very_longer_variable12)&&(very_longer_variable3<=very_longer_variable14)&&(very_longer_variable5<=very_longer_variable16)){dosomething();}virtualCMatrixCMultiplyMatrix(CMatrixleftMatrix,CMatrixrightMatrix);for(very_long_initialization;very_long_condition;very_long_update){dosomething();}示例2-5长行的拆分修饰符的位置【规则2-6-1】将修饰符*和&紧靠变量名,以免引起误解。例如:char*name;int*x;注释C语言的注释符为“/*…*/”和“//”。注释通常用于:(1)版本、版权声明;(2)函数接口说明,包括参数类型和意义、函数类型和意义等;(3)重要的数据类型声明、变量、算法、处理、段落等提示。 “//”为行注释。【规则2-7-1】注释是对代码作用的“提示”,而不是文档。注释的频度要合适,一般要求占程序总行数的1/5~1/4。【规则2-7-2】边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。【规则2-7-3】注释应准确、易懂,防止注释有二义性。错误的注释不但无益反而有害。【规则2-7-4】尽量避免在注释中使用缩写,特别是不常用的缩写。【规则2-7-5】注释的位置应与被描述代码相邻,可以放在代码的上方或右方,一般不宜【规则2-7-6】对于所有有物理含义的变量、常量、数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,在声明时都必须加以注释。示例:/*activestatistictasknumber*/#defineMAX_ACT_TASK_NUMBER1000【规则2-7-7】全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明【规则2-7-8】注释与所描述内容进行同样的缩排。【规则2-7-9】将注释与其上面的代码用空行隔开。【规则2-7-10】对变量的定义和分支语句(条件分支、循环语句等)必须编写注释。【规则2-7-11】对于switch语句下的case语句,如果因为特殊情况需要处理完一个case后进入下一个case处理,必须在该case语句处理完、下一个case语句前加上明确的注释。说明:这样比较清楚程序编写者的意图,有效防止无故遗漏break语句【规则2-7-12】当代码段较长时,在程序块的结束行右方加注释标记,以表明某程序块的结束。说明:当代码段较长,特别是多重嵌套时,这样做可以使代码更清晰,更便于阅读。示例:参见如下例子。if(...){//programcodewhile(index<MAX_INDEX){//programcode}/*endofwhile(index<MAX_INDEX)*///指明该条while语句结束}/*endofif(...)*///指明是哪条if语句结束【规则2-7-13】当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。【规则2-7-14】尽量不要在语句指令中添加注释。【规则2-7-15】注释不具备约束使用者行为的能力。【规则2-7-16】给一行代码添加注释最好使用“//”,比较清楚。标识符命名共性规则是被大多数程序员采纳的,我们应当在遵循这些共性规则。命名两个基本原则:1.含义清晰,不易混淆;2.不与其它模块、函数的命名空间相冲突。【规则3-1-1】标识符要清楚、准确、简单而且尽量可发音的完整英文单词或大家基本可理解的缩写,避免使人产生误解。如使用特殊约定或缩写,则要有注释说明。 例如:intreturnStatus; 不要把currentValue写成nowValue。【规则3-1-2】标识符的长度应当符合“min-length&&max-information”(最短并包含信息最多)原则。单字符的名字也是有用的,常见的如i、j、k、m、n、x、y、z等,它们通常可用作函数内的局部循环变量。【规则3-1-3】命名规则尽量与所采用的操作系统或开发工具的风格保持一致,并在同一项目中统一。例如Windows应用程序的标识符通常采用“大小写”混排的方式,如printStudent;而Unix应用程序的标识符通常采用“小写加下划线”的方式,如print_student。别把这两类风格混在一起用。【规则3-1-4】尽量选择通用词汇并保持整个软件风格一致例如:使用get、read、fetch、retrieve都能表达“取出”的意思,一旦软件采用哪一个则应贯穿始终。【规则3-1-5】 例如:intx,X;/*变量x与X容易混淆*/ voidfoo(inty);/*函数foo与FOO容易混淆*/voidFOO(floaty);【规则3-1-6】程序中不要出现标识符完全相同的局部变量和全局变量,尽管可能【规则3-1-7】 例如: floatvalue; floatnewValue;【规则3-1-8】说明:下面是一些在软件中常用的反义词组。add/remove begin/end create/destroyinsert/delete first/last get/releaseincrement/decrement put/getadd/delete lock/unlock open/closemin/max old/new start/stopnext/previous source/target show/hidesend/receive source/destinationcut/paste up/down 例如: tk_uint16uiMinValue; tk_uint16uiMaxValue; tk_uint16MinValue(void); tk_uint16MaxValue(void);【规则3-1-9】局部变量和参数首字母或前两字母用于表明类型并小写,每个英文单词的第一个字母大写,其它小写,参见附录A《【规则3-1-10】常量前加前缀c_,下划线后命名规则同局部变量定义规则。 consttk_uint16c_uiMaxLength=100;【规则3-1- #defineMAX_LENGTH100;【规则3-1- #defineF_HEX2BCD(x)(((x)/10)<<4)+((x)%10)//转bcdx加括号【规则3-1-13】静态变量加前缀s_(表示static)。 staticints_initValue;/*静态变量*/【规则3-1-14】如果需要定义全局变量,则变量加前缀g_(表示global)。 例如:intg_howStudent;/*全局变量*/【规则3-1-15】函数名用大写字母开头的单词组合而成。由多个单词组成的标识符每个单词首字母大写。其它小写。 例如:InputStudentInfo();//全局函数【规则3-1-16】尽量避免名字中出现数字编号,如value1、value2等,除非逻辑上的确需要编号。常量常量是一种标识符,它的值在运行期间恒定不变。C语言用#define来定义常量。除了#define之外还可以用const来定义常量。const与#define的比较C语言可以用const来定义常量,也可以用#define来定义常量。但是前者比后者有更多的优点:(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换过程中可能会产生意料不到的错误。(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。【规则4-1-1】 例如:#defineMAX100//C语言的宏常量 constfloatPI=3.14159;//C语言的const常量【规则4-1-3】尽量使用const定义常量替代宏定义常量。常量定义【规则4-2【规则4-2-2】 例如: constfloatRADIUS=100; constfloatDIAMETER=RADIUS*2;【规则4-2-3】【规则4-2-4】避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。示例:如下的程序可读性差。if(Trunk[index].trunk_state==0){Trunk[index].trunk_state=1;...//programcode}应改为如下形式。#defineTRUNK_IDLE0#defineTRUNK_BUSY1if(Trunk[index].trunk_state==TRUNK_IDLE){Trunk[index].trunk_state=TRUNK_BUSY;...//programcode}【规则4-2-5】如果宏值多于一项,一定使用括号。 例如:#defineERROR_DATA_LENGTH10+1 应该这样定义: #defineERROR_DATA_LENGTH(10+1) 这样使用malloc(5*ERROR_DATA_LENGTH)时,得到是5*(10+1)=55;而上面的定义则得到5*10+1=51。【规则4-2-6】函数宏的每个参数都要括起来。 例如:#defineWEEKS_TO_DAYS(w)(w*7) 应该写成:#defineWEEKS_TO_DAYS(w)((w)*7) 这样在翻译totalDays=WEEKS_TO_DAYS(1+2)时,才能够正确地翻译成:(1+2)*7;否则将错误地翻译成1+2*7。变量【规则5-1-【规则5-1-2】防止局部变量与公共变量同名。【规则5-1-3】局部变量在引用之前要进行出初始化或要有明确的值。【规则5-1-4】如果指针变量知道被初始化为什么地址,则初始化为该地址,否则初始化为NULL。【规则5-1-5】所有的外部变量声明前都应加上extern关键字。【规则5-1-6】尽量不要使用一个bit位控制程序流程或标识特定状态。最好使用多位或枚举类型标识状态。【规则5-1-7】如果定义数组时全部初始化,则不用给出数组长度。 例如:intarray[]={1,2,3,4,5};在需要使用数组长度时,用sizeof(array)/sizeof(array[0])计算得出。【规则5-1-8】不同文件的全局变量没有固定的初始化顺序,注意使用#include包括的文件都算作同一文件。【规则5-1-9】尽量避免强制类型转换;如果不得不使用,则尽量使用显式方式。【规则5-1-10】尽量少的使用浮点类型,因为浮点数据标识不精确,而且运算速度慢。【规则5-1-11】尽量少用union类型,因为成员共用内存空间,处理不当容易出错。结构体【规则6-1-1】结构体的功能要单一,是针对一种事务的抽象。说明:设计结构时应力争使结构代表一种现实事务的抽象,而不是同时代表多种。结构中的各元素应代表同一事务的不同侧面,而不应把描述没有关系或关系很弱的不同事务的元素放到同一结构中。示例:如下结构不太清晰、合理。typedefstructSTUDENT_STRU{unsignedcharname[8];/*student'sname*/unsignedcharage;/*student'sage*/unsignedcharsex;/*student'ssex,asfollows*//*0-FEMALE;1-MALE*/unsignedcharteacher_name[8];/*thestudentteacher'sname*/unisgnedcharteacher_sex;/*histeachersex*/}STUDENT;若改为如下,可能更合理些。typedefstructTEACHER_STRU{unsignedcharname[8];/*teachername*/unisgnedcharsex;/*teachersex,asfollows*//*0-FEMALE;1-MALE*/}TEACHER;typedefstructSTUDENT_STRU{unsignedcharname[8];/*student'sname*/unsignedcharage;/*student'sage*/unsignedcharsex;/*student'ssex,asfollows*//*0-FEMALE;1-MALE*/unsignedintteacher_ind;/*histeacherindex*/}STUDENT;【规则6-1-2】不要设计面面俱到、非常灵活的数据结构。【规则6-1-3】不同结构间的关系不要过于复杂。说明:若两个结构间关系较复杂、密切,那么应合为一个结构。示例:如下两个结构的构造不合理。typedefstructPERSON_ONE_STRU{unsignedcharname[8];unsignedcharaddr[40];unsignedcharsex;unsignedcharcity[15];}PERSON_ONE;typedefstructPERSON_TWO_STRU{unsignedcharname[8];unsignedcharage;unsignedchartel;}PERSON_TWO;由于两个结构都是描述同一事物的,那么不如合成一个结构。typedefstructPERSON_STRU{unsignedcharname[8];unsignedcharage;unsignedcharsex;unsignedcharaddr[40];unsignedcharcity[15];unsignedchartel;}PERSON;【规则6-1-4】结构中元素的个数应适中。若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数。说明:增加结构的可理解性、可操作性和可维护性。示例:假如认为如上的_PERSON结构元素过多,那么可如下对之划分。typedefstructPERSON_BASE_INFO_STRU{unsignedcharname[8];unsignedcharage;unsignedcharsex;}PERSON_BASE_INFO;typedefstructPERSON_ADDRESS_STRU{unsignedcharaddr[40];unsignedcharcity[15];unsignedchartel;}PERSON_ADDRESS;typedefstructPERSON_STRU{PERSON_BASE_INFOperson_base;PERSON_ADDRESSperson_addr;}PERSON;【规则6-1-5】结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应用保留余地(如预留一些空间等)。说明:软件向前兼容的特性,是软件产品是否成功的重要标志之一。如果要想使产品具有较好的前向兼容,那么在产品设计之初就应为以后版本升级保留一定余地,并且在产品升级时必须考虑前一版本的各种特性。表达式和基本语句运算符的优先级【规则7-1-1】避免使用默认的优先级。如果代码行中的运算符比较多,为了防止产生歧义并提高可读性,应当用括号明确表达式的计算顺序。 例如:value=(high<<8)|low if((a|b)&&(a&c))复合表达式【规则7-2-1】不允许使用复合表达式。if语句布尔表达式if语句是C语言中最简单、最常用的语句,然而很多程序员用隐含错误的方式写if语句。【规则7-3-1】如果布尔表达式比较长且与一个常值进行比较时,一般把常值放在前面更清楚。 例如: if(0!=(a+b)%6) { }【规则7-3-2】有多个if语句嵌套时,要层层对齐,每一层的真假分支使用括号括起来并对齐。 例如:if(x>y){}else{if(y>z){}else{}}【规则7-3-3】布尔表达式中有多个逻辑“与”判断条件时,只要其中一个条件不满足则该表达式的值就是“假”;注意在else分支中,不能假定某个逻辑表达式为“真”而进行处理,导致错误。 例如:inta=10;intb=0;intc=0;if(0!==a&&0!=b&&0!=c){}else{x=y/c;}【规则7-3-4】布尔表达式中有多个逻辑“或”判断条件时,只要其中一个条件满足则该表达式的值就是“真”;注意不能假定某个逻辑表达式为“真”而进行处理,导致错误。 例如:intinta=10;intb=0;intc=0;if(0!==a||0!=b||0!=c){x=y/c;}【规则7-3-5】不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准。例如VisualC++将TRUE定义为1,而VisualBasic则将TRUE定义为-1。假设布尔变量名字为flag,它与零值比较的标准if语句如下:if(flag)/*表示flag为真*/if(!flag)/*表示flag为假*/其它的用法都属于不良风格,例如:if(flag==TRUE)if(flag==1)if(flag==FALSE)if(flag==0)【规则7-3-6】不可将浮点变量用“==”或“!=”与其它变量或数字比较。千万要留意,无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”的形式。假设浮点变量为x,应当将 if(x==0.0)//隐含错误的比较转化为 if((x>=EPSINON)&&(x<=EPSINON))其中EPSINON是允许的误差(即精度)。【规则7-3-7】应当将指针变量用“==”或“!=”与NULL比较。指针变量的零值是“空”(记为NULL)。尽管NULL的值与0相同,但是两者意义不同。假设指针变量的名字为p,它与零值比较的标准if语句如下:if(p==NULL)//p与NULL显式比较,强调p是指针变量if(p!=NULL)不要写成if(p==0)//容易让人误解p是整型变量if(p!=0)或者if(p)//容易让人误解p是布尔变量if(!p)循环语句C语言的循环语句中,for语句的使用频率最高,while语句其次,do语句很少用。本节重点论述循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。【规则7-4-1】不可在循环体内修改循环变量,以防止循环失去控制。【规则7-4-2】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。如下表的对比:低效率:长循环在最外层高效率:长循环在最内层for(row=0;row<100;row++){for(col=0;col<5;col++){sum=sum+a[row][col];}}for(col=0;col<5;col++){for(row=0;row<100;row++){sum=sum+a[row][col];}}【规则7-4-3】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。下面示例a的程序比示例b程序多执行了N-1次逻辑判断。并且由于前者总要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果N非常大,最好采用示例b的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例a的写法比较好,因为程序更加简洁。如下表的对比:a、效率低但程序简洁b、效率高但程序不简洁for(i=0;i<N;i++){if(condition)DoSomething();elseDoOtherthing();}if(condition){for(i=0;i<N;i++)DoSomething();}else{for(i=0;i<N;i++)DoOtherthing();}【规则7-4-4】建议for语句的循环控制变量的取值采用“半开半闭区间”写法。示例a中的x值属于半开半闭区间“0=<x<N”,起点到终点的间隔为N,循环次数为N。示例b中的x值属于闭区间“0=<x<=N-1”,起点到终点的间隔为N-1,循环次数为N相比之下,示例a的写法更加直观,尽管两者的功能是相同的。a、循环变量属于半开半闭区间b、循环变量属于闭区间for(x=0;x<N;x++){...}for(x=0;x<=N-1;x++){...}switch语句【规则7-5-1】每个case语句的结尾不要忘了加break语句,否则将导致多个分支重叠(除非有意使多个分支重叠)。【规则7-5-2】不要忘记最后的default分支。即使程序真的不需要default处理,也应该保留语句default:break;goto语句【规则7-6-1】慎用goto语句。虽然在某些情况使用方便,但是会造成程序可读性下降。函数设计注释规则【规则8-1-1】一般在函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等注释信息。可以选择在函数末尾添加该函数在单元测试中曾经出现的问题。函数的使用【规则8-2-1】减少函数本身或函数间的递归调用。函数名【规则8-3-1】函数名应准确描述函数的功能。【规则8-3-2】使用动宾词组为执行某操作的函数命名。如果是OOP方法,可以只有动词(名词是对象本身)。示例:参照如下方式命名函数。voidPrintRecord(unsignedintrec_ind);intInputRecord(void);unsignedcharGetCurrentColor(void);【规则8-3-3】避免使用无意义或含义不清的动词为函数命名。说明:避免用含义不清的动词如process、handle等为函数命名,因为这些动词并没有说明要具体做什么。参数的规则【规则8-4-1】参数的书写要完整,不要贪图省事只写参数的类型而省略参数的名字。如果函数没有参数,则用void填充。(使用函数原型)。例如:voidSetValue(intwidth,intheight);//良好的风格voidSetValue(int,int);//不良的风格floatGetValue(void);//良好的风格floatGetValue(); //不良的风格【规则8-4-2】参数命名要恰当,顺序要符合习惯用法。例如:编写字符串拷贝函数CopyString,它有两个参数。如果把参数名字起为str1和str2:voidCopyString(char*str1,char*str2);很难搞清楚究竟是把str1拷贝到str2中,还是相反。可以把参数名字起得更有意义,例如strSource和strDestination。这样从名字上就可以看出应该把strSource拷贝到strDestination中。另外,参数的顺序要遵循库函数的风格。一般地,应将目的参数放在前面,源参数放在后面。【规则8-4-3】如果参数是指针,且仅作输入用,则应在类型前加const,以防止该指针在函数体内被意外修改。例如:voidStringCopy(char*strDestination,constchar*strSource);【规则8-4-4】对于基本数据类型参数应该传递值(除非函数要修改传入的参数),这样安全、简单。对于复合数据类型应传递指针(地址),以提高效率。【规则8-4-5】防止将函数的参数作为工作变量。说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。示例:下函数的实现不太好。voidsum_data(unsignedintnum,int*data,int*sum){unsignedintcount;*sum=0;for(count=0;count<num;count++){*sum+=data[count];//sum成了工作变量,不太好。}}若改为如下,则更好些。voidsum_data(unsignedintnum,int*data,int*sum){unsignedintcount;intsum_temp;sum_temp=0;for(count=0;count<num;count++){sum_temp+=data[count];}*sum=sum_temp;}【规则8-4-6】非调度函数应减少或防止控制参数,尽量只使用数据参数。说明:本建议目的是防止函数间的控制耦合。调度函数是指根据输入的消息类型或控制命令,来启动相应的功能实体(即函数或过程),而本身并不完成具体功能。控制参数是指改变函数功能行为的参数,即函数要根据此参数来决定具体怎样工作。非调度函数的控制参数增加了函数间的控制耦合,很可能使函数间的耦合度增大,并使函数的功能不唯一。示例:如下函数构造不太合理。intadd_sub(inta,intb,unsignedcharadd_sub_flg){if(add_sub_flg==INTEGER_ADD){return(a+b);}else{return(a–b);}}不如分为如下两个函数清晰。intadd(inta,intb){return(a+b);}intsub(inta,intb){return(a-b);}【规则8-4-7】避免函数有太多的参数,参数个数尽量控制在5个以内。如果参数太多,在使用时容易将参数类型或顺序搞错。【规则8-4-8】尽量不要使用类型和数目不确定的参数。返回值的规则【规则8-5-1】不要省略返回值的类型。如果函数没有返回值,那么应声明为void类型。【规则8-5-2】函数名字与返回值类型在语义上不可冲突。【规则8-5-3】函数返回正常值和错误标志要有明确的说明。【规则8-5-4】给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。【规则8-5-5】返回指针类型的函数应该使用NULL表示失败。函数内部实现的规则不同功能的函数其内部实现各不相同,看起来似乎无法就“内部实现”达成一致的观点。但根据经验,我们可以在函数体的“入口处”和“出口处”从严把关,从而提高函数的质量。【规则8-6-1】在函数体的“入口处”,对所有参数输入的有效性进行检查。【规则8-6-2】尽量保持函数只有一个出口,在函数体的“出口处”,对return语句的正确性和效率进行检查。注意事项如下:(1)return语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁。例如:char*Func(void) { charstr[]=“hellostudent”;//str的内存位于栈上 …returnstr;//将导致错误 }(2)要搞清楚返回的究竟是“值”还是“指针”。其它建议【规则8-7-1】函数的功能要单一,不要设计功能过于复杂的函数。【规则8-7-2】经常使用的重复代码用函数来代替。【规则8-7-3】函数体的规模要小,尽量控制在200行代码之内(不包括注释和空行)。【规则8-7-4】尽量避免函数带有“记忆”功能。相同的输入应当产生相同的输出。函数的static局部变量是函数的“记忆”存储器。建议尽量少用static局部变量,除非必需。【规则8-7-5】不仅要检查输入参数的有效性,还要检查通过其它途径进入函数体内的变量的有效性。 例如全局变量、文件句柄等。【规则8-7-6】用于出错处理的返回值一定要清楚,让使用者不容易忽视或误解错误情况。内存管理【规则9-1-1】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。【规则9-1-2】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。可测性【规则10-1-1】在同一项目组或产品组内,要有一套统一的为集成测试与系统联调准备的调测开关及相应打印函数,并且要有详细的说明。【规则10-1-2】在同一项目组或产品组内,调测打印出的信息串的格式要有统一的形式。信息串中至少要有所在模块名(或源文件名)。【规则10-1-3】在进行集成测试/系统联调之前,要构造好测试环境、测试项目及测试用例,同时仔细分析并优化测试用例,以提高测试效率。说明:好的测试用例应尽可能模拟出程序所遇到的边界值、各种复杂环境及一些极端情况等。【规则10-1-4】正式软件产品中应把调测代码去掉(即把有关的调测开关关掉)。【规则10-1-5】在软件系统中设置与取消有关测试手段,不能对软件实现的功能等产生影响。【规则10-1-6】用调测开关来切换软件的DEBUG版和正式版,而不要同时存在正式版本和DEBUG版本的不同源文件,以减少维护的难度。【规则10-1-7】软件的DEBUG版本和发行版本应该统一维护,不允许分家,并且要时刻注意保证两个版本在实现功能上的一致性。程序效率程序的时间效率是指运行速度,空间效率是指程序占用内存或者外存的状况。全局效率是指站在整个系统的角度上考虑的效率,局部效率是指站在模块或函数角度上考虑的效率。【规则11-1-1】不要一味地追求程序的效率,应当在满足正确性、可靠性、健壮性、可读性等质量因素的前提下,设法提高程序的效率。【规则11-1-2】以提高程序的全局效率为主,提高局部效率为辅。【规则11-1-3】在优化程序的效率时,应当先找出限制效率的“瓶颈”,不要在无关紧要之处优化。【规则11-1-4】先优化数据结构和算法,再优化执行代码。【规则11-1-5】有时候时间效率和空间效率可能对立,此时应当分析哪个更重要,做出适当的折衷。例如多花费一些内存来提高性能等。【规则11-1-6】不要追求紧凑的代码,因为紧凑的代码并不能产生高效的机器码。【规则11-1-7】循环体内工作量最小化。【规则11-1-8】要仔细地构造或直接用汇编编写调用频繁或性能要求极高的函数。编译问题【规则12-1-1】把编译器的选择项设置为最严格状态。在调试代码时应该将编译器的所有警告信息都打开,并且编译结果应不包括任何警告信息。把问题尽量暴露在编译时而不是运行时,尽量减少解决的代价。【规则12-1-2】对于大型软件尽量减少编译时间。例如调试时关闭优化选项,尽量减少文件之间的依赖程度。【规则12-1-3】某些语句经编译后产生告警,但如果你认为它是正确的,那么应通过某种手段去掉警信息。兼容性问题【规则13-1-1】遵守ANSI国际标准,将不符合标准的代码与其它代码分开并进行标记,将依赖特定硬件或操作系统的代码分开。【规则13-1-2】注意数据文件的兼容性,例如:字节排列顺序,数据对齐方式,行尾标志等。【规则13-1-3】尽量使用标准库函数,不要“发明”已经存在的库函数。代码审查【规则14-1-1】推荐使用代码检查工具(如C语言用PC-Lint)对源程序检查。【规则14-1-2】推荐使用软件工具(如LogiSCOPE)进行代码审查。代码测试、维护【规则15-1-1】单元测试要求至少达到语句覆盖。【规则15-1-2】单元测试开始要跟踪每一条语句,并观察数据流及变量的变化。【规则15-1-3】清理、整理或优化后的代码要经过审查及测试。【规则15-1-4】代码版本升级要经过严格测试。【规则15-1-6】正式版本上软件的任何修改都应有详细的文档记录。【规则15-1-7】发现错误立即修改,并且要记录下来。【规则15-1-9】仔细设计并分析测试用例,使测试用例覆盖尽可能多的情况,以提高测试用例的效率。【规则15-1-10】尽可能模拟出程序的各种出错情况,对出错处理代码进行充分的测试。【规则15-1-11】仔细测试代码处理数据、变量的边界情况。【规则15-1-12】保留测试信息,以便分析、总结经验及进行更充分的测试。【规则15-1-13】不应通过“试”来解决问题,应寻找问题的根本原因。【规则15-1-14】对自动消失的错误进行分析,搞清楚错误是如何消失的。【规则15-1-15】修改错误不仅要治表,更要治本。【规则15-1-16】测试时应设法使很少发生的事件经常发生。【规则15-1-17】测试时应明确模块或函数处理哪些事件,并使它们经常发生。【规则15-1-18】坚持在编码阶段就对代码进行彻底的单元测试,不要等以后的测试工作来发现问题
附录A:通卡标准类型头文件及变量定义要求统一类型定义要求typedefchartk_int8;typedefunsignedchartk_uint8;typedefshorttk_int16;typedefunsignedshorttk_uint16;typedeflongtk_int32;typedefunsignedlongtk_uint32;typedeflonglongtk_int64;typedefunsignedlonglongtk_uint64;typedefvolatilechartk_vint8;typedefvolatileunsignedchartk_vuint8;typedefvolatileshor
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 公司员工个人工作总结例文8篇
- 体育老师个人教学工作总结范文
- 初中英语教师工作计划大全
- 2024年度施工现场临时用电安全管理及施工人员安全培训协议3篇
- 爱心助学捐款倡议书9篇
- 2022年幼儿园支部工作计划
- 经典电影全城高考观后感
- 六上分数除法解决问题例7
- 怎样拯救我们的脑课件
- 2024届河南省息县高三下学期三校联考高考一模物理试卷
- 第26课《诗词五首:春望》教学实录 统编版语文八年级上册
- 田径大单元教学计划
- 物理化学题库(含答案)
- 嵌入式软件设计方案
- 2023年云南滇中新区事业单位招聘30人笔试参考题库(共500题)答案详解版
- 华为财务管理(6版)-华为经营管理丛书
- 语言领域核心经验学前儿童语言学习与发展核心经验
- 第七课经济全球化与中国学案高中政治选择性必修一当代国际政治与经济
- 中国传统制墨工艺研究
- 七氟丙烷出厂检验报告范本
- 西游记(猪八戒)课件
评论
0/150
提交评论