老万聊编程—C/C++编码规范
在阅读别人的代码时,你是否遇到这样的情况;看到一个字符串不知道它是局部变量,或是全局变量以及该字符串表示的含义等,你必须反复的查看该字符串表示的含义,这样几次之后你的感觉如何?现在思考下微软的软件为什么被广泛的使用?用过WindowsAPI编程都知道,它的名字非常好记,它的程序可读性非常强,编写软件不仅要实现功能,还要追求软件的艺术,给人一种美的享受。对初学编程的人来说,常常用inta,b等对变量的命名;对函数的命名采用的是f(),g()等。用这种方法写出的程序有什么优点吗?在个人英雄主义时代便于程序的保密,在当今合作的时代,这样的程序犹如鸡肋,对这样的程序我的处理方法为:要勇敢的抛弃,免受折磨。怎样使一个项目组中的任何成员看懂别人的程序及修改别人的程序。我通过几年的工作经验与教训及借鉴他人的经验总结出了编码规范共大家参考。
编码规范是由实际经验得到的常识,它不是随意的规则或处方。代码应该是清楚的、简单的、具有直截了当的逻辑、自然的表达公式、通行的语言使用方式、有意义的名字和有帮助作用的注释等。同时编码规范对程序的兼容性,排错性以及性能的提高等都有很大的影响。好的开始是成功的一半,好的编码规范是成功的关键。闲话少说,现在我从以下几个方面来说明编码规范。
名字的命名原则:尽量用少的字符精确的表达出其内容一个变量、函数以及文件的名字标识了这个对象,同时也携带着其用途的一些信息。因此对变量、函数、文件等命名要遵守一定的规则。
.要用英文命名。在软件开发中英语是一种通用的语言,尽量避免使用汉语拼音。汉语有几千年的历史博大精深,存在着大量的同音字或同音词,据统计读YI的有一百多个,所以仅靠汉语拼音是很难识别出具体的含义。
.名字的长短与其作用域成正比,作用域越大名字越长。例如全局变量的名字要比局部变量的名字长,全局函数要比成员函数的名字长等。
.3尽量选用通用的词汇,并贯穿始终。例如Get,Obtain都有“得到”的意思,但Get比Obtain更常用些。不要选用一些比较长、难记的单词,在命名时牢记这里是编程,方便与别人交流,不是英语的专业考试。
.4最好使用能够拼读的词汇,便于交流。.5不要使用不标准的缩写或自定义的缩写。在一个项目组中可以规定一些在项目开发中常用的缩写,但不要太多,对标准的缩写不要重新定义。
.6名字尽量能表达出其表示的内容。例如判断一个charc是否为字符,IsChar(c)比CheckChar(c)好。
.7避免使用容易混淆的字母或数字。例如大写字母O与数字0,小写字母l与数字等。
.8书写规则为每个单词的首字母用大写,其余的字母小写。例如GetTime。有的采用下画线“_”(例如,get_time),我认为这种格式书写不方便。
命名规则在C/C++编程中,全局变量可以和局部变量重名,函数名可以和局部变量重名,标号也可以与局部变量重名等一些重名的现象。重名对阅读程序,调试带来了巨大的困难,我们在编程要尽量避免重名现象。对于航天型号的C语言程序严格规定了以下重名现象。
)严禁过程名(函数名)被用于其他处,例如函数名与局部变量重名。
)严禁标号名被用于其他处。
3)严禁全局变量与局部变量重名。
4)严禁对C/C++关键字的重新定义。
为了避免重名现象、提高程序的可靠性、性能、书写的规范性等,现在对各种命名做如下规定。
3变量与标号的命名规则对于变量的名字能够表示出其作用域、类型及含义,在C/C++语言中任何一个变量都有其作用域、变量类型及变量所表示的含义等。下面给出变量的命名规则:
3.变量的作用域:根据变量的作用域可把变量分为:局部变量、静态变量、全局变量、类的成员变量、常量型变量,寄存器变量等。对于不同类型的变量要加以区别。区别如下:
)局部变量:变量类型(小写)+名字
)静态变量:加s_s表示static
3)全局变量:加g_g表示global
4)类的成员变量:加m_m表示member
5)常量型变量:加c_c表示constant
6)标号命名:加L_L表示label;注意L用大写避免小写字母l与数字混淆。
7)寄存器变量不建议使用。
3.变量的类型:)字符串类型:加前缀str(string)
)宽字符串类型:加前缀wstr(widestring)
3)字节类型:加前缀c(char)
4)无符号字节类型:加前缀uc(unsingnedchar)
5)指针类型:加前缀p(point)
6)布尔类型:加前缀b(bool)
7)短整型:加前缀s(short)
8)无符号短整型:加前缀us(unsignedshort)
9)对于int型:加前缀n(integer)
0)对于无符号的int型:加前缀un(unsignedinteger)
)对于long型:加加前缀l(long)
)对于无符号的long型:加前缀ul(unsignedlong)
3)对于浮点类型:加前缀f(float)
4)对于双精度浮点类型:加前缀d(double)
5)对于类对象:加前缀cls(class)
6)对于结构变量:加前缀st(struct)
7)句柄类型:加前缀h(handle)
8)对于文件流变量:加前缀pf(pointfile)等。
3.3变量的命名规则:
变量的作用域——变量类型——变量名字。
例如:intg_nStatSum=0表示统计总和的变量,该变量的类型为int型,作用域为全局变量。
constfloatc_fPI=3.表示PI值,该值为常量类型。
FILE*pfSource含义为指向源文件流,该文件流为局部变量。
4、宏命名与使用:尽量避免使用宏4.常数宏的命名:加前缀M_,后面名字全部大写在C++可以用常量定义替代,因为常量定义可以执行类型检查具有更大的安全性。在调试中可以读取常量的值,一般不能直接读取宏定义的值。
例如:#defineM_SCREENX04
在C++中定义为:constintc_iScreenX04;
4.在写宏时最后语句不要加分号因为宏执行的是替代操作,若加分号可能会引起程序的混乱。
例如:#defineM_MAXNUM00;
for(intn=0;nM_MAXNUM;n++)
会出现什么情况,大家可以实验。
4.3函数宏的命名:加前缀MF_,后面名字全部大写例如:#defineMF_SQUARE(X)
对函数宏的使用要注意以下事项:
)尽量避免使用函数宏。在C++里可以用内联函数来替代。即使在C语言里,他带来的麻烦比解决问题更多。函数宏最常见的一个严重问题是:如果一个参数在定义中出现多次,它就可能被多次求值。如果调用时的实际参数带有副作用,结果将会产生一个难以琢磨的错误。例如:
#defineMF_ISUPPER(C)((C)=’A’((C)=’Z’)
如果这样调用:while(MF_ISUPPER(c=getchar())会出现??
改正:while((c=getchar())!EOFMF_ISUPPER(c))
)如果使用宏,要给宏的体和参数都加上括号。
例如:#defineMF_SQUARE(X)(X)*(X)
对于下面的表达式:/MF_SQUARE(X),出现什么结果?
改正:#defineMF_SQUARE(X)((X)*(X))
例如:#defineMF_SQUARE(X)(X*X)
对于下面的表达式:MF_SQUARE(X+),出现什么结果?
改正:#defineMF_SQUARE(X)((X)*(X))
3)一定要用{}将具有多行函数宏的函数体括起来。
4.4彻底用typedef代替宏定义新类型例如:#deineMT_PINTint*
typedefPINTint*;
4.5禁止在同一个宏中使用多个#或##在同一个宏中使用多于一个的#或##,或同时使用#和##都是很危险的
4.6禁止重新定义保留字宏预处理可以重新定义保留字,但这种做法会引起混淆,因此禁止重新定义保留字。
例如:#defineFILEunsignedint(不允许)
4.7避免在一个程序块中单独使用#define和#undef例如:这是禁止的:
intForbidUse(void)
{
#defineM_EXAMPLE00
}
4.8谨慎使用#pragma#pragma可以用来掩盖所有类型的问题,因此应谨慎使用#pragma。
5对于常数的命名与使用5.用enum取代(一组相关的)常量,具有明确的意义。例如:typedefenum
{
DIB_BPP,//色图像
DIB_BPP,//4色图像
DIB_4BPP,//6色图像
DIB_4BPPRLE,//6色图像,RLE压缩格式
……………………
}DIBFormat;
对于enum保证全部赋值,方法为:
)全部不赋值
)第个赋值,其余全部不赋值。
3)全部赋值
5.在C++中把数定义为常数,不要定义为宏。在VC编译器下,定义为宏的常数在调试状态下无法获取该值且不执行类型检查。
例如:C++里定义常数为constintc_nScreenX=04。
5.3使用字符形式的常量,不要使用整数。人们常用到ctype.h里的函数。
例如:if(cChar=65cChar=90)等价于if(cChar=’A’cChar=’Z’)
对字符串赋0要用’/0’不要用0
例如strName[0]=0等价于strName[0]=’/0’
5.4利用语言去计算对象的大小。不要对任何数据类型使用显示写出来大小,例如用代替sizeof(short),基于同样的原因,写sizeof(aArray[0])可能比sizeof(int)更好,因为即使数组的类型改变了,也不用改变程序。原则是:能清楚地表明对象的类型,即使当变量改动时程序改动最小。
6、指针的命名与使用6.指针的变量的前面加小写的字母p例如short*psData;//p表示指针类型,s表示short类型,所以为表示指向short类型的指针。
6.用new、delete取代malloc、calloc、realloc和free因为malloc、calloc、realloc和free是C语言的用法,它们不会理会到对象是否存在,更不会去调用构造和析构函数,所以在C++中经常引起麻烦。
6.3new、delete和new[]、delete[]要成对使用▲调用new所包含的动作:
)从系统堆中申请恰当的一块内存
)若是对象,调用相应类的构造函数,并以刚申请的内存地址作为this参数。
▲调用new[]所包含的动作:
)从系统堆中申请可容纳n个对象外加一个整型的一块内存。
)将n记录在额外的那个整型内存中(其位置依赖于不同的实现,有的在申请内存块的开头,有的末尾)。
3)调用n次构造函数初始化这块内存中的n个连续对象。
▲调用delete所包含的动作:
)若是对象,调用相应类的析构函数(在delete参数所指的内存处)
)将内存返还系统堆。
▲调用delete[]所包含的动作:
)从new[]记录n的地方将n值找出。
)调用n次析构函数析构这块内存中n个连续的对象。
3)将这一整块内存(包括记录n的整数)还系统堆。
6.4指针应该有一个合理的值在指针使用前必须有一个合理值,释放后必须把指针置为无效。
例如:
int*piData=NULL;
piData=newint[04];
……………………..
………………………
//删除操作
if(piData!=NULL)
{
delete[]piData;
piDada=NULL;
}
达到两种目的:
)防止其后再次使用该指针。
)防止其后再次删除该指针。
6.5牢记给字符串结束符申请空间例如:char*pstrNamer=newchar[strlen+];
6.6指针的运算规则)禁止将参数指针赋值给函数指针。
)尽量避免使用三重指针。
对指针进行控制是很困难的,当指针的指针超过两级时,使用起来更是具有风险,因此禁止指针的指针超过两级。
3)尽量避免使用函数指针。
使用过程指针是具有较大风险的,因此禁止将过程声明为指针类型。
4)谨慎使用指针的逻辑比较
使用大于和小于的操作符对指针进行比较是具有较大风险的,应谨慎使用指针的逻辑比较。
5)谨慎对指针进行代数运算
对指针进行代数运算是具有较大风险的,应谨慎对指针进行代数运算。
6.7遵守谁申请谁释放的原则。7表达式的书写规则7.谨慎使用三重表达式例如nMax=nDatanData?nData:nData;
7.禁止对有符号类型进行移位运算对有符号类型进行移位运算会导致不可预料的后果,对变量进行移位运算必须保证不会产生溢出,一些编译器不检查移位运算是否超出机器字长。
7.3禁止给无符号变量赋负值给无符号变量赋负值会导致不可预料的结果,因此禁止给无符号变量赋负值。
7.4使用表达式的自然形式。例如:含有否定运算的条件表达式比较难理解:
if(!(ab)
!(cd))
应写为:
if(a=b)
(c=d))更好理解。
因为人类的思维习惯一般为正向思维,对于逆向思维来说需要多转一个弯,给审查程序者带来不方便。
7.5用加括号的方式排除二义性及增加可读性例如:if(xMASK==BITS)实际上意味着if(x(MASK==BITS)。这个表达式肯定不是程序员的本意。
程序员的本意为if((xMASK)==BITS)。
7.6分解复杂的表达式,把复杂的表达式分解成多个简单的表达式。7.7表达式要清晰:我们的目标是写出最清晰的代码,而不是最巧妙的代码。
例如:subkey=subkey(bitoff-(bitoff3)3)。
等价于:subkey=subkey(bitoff0x7);
7.8当心副作用。像++这一类运算符具有副作用,他们除了返回一个值外,还将隐含的改变变量的值。
例如:str[i++]=str[i++]=’‘;这样写意图是给str中随后的两个位置赋空格,但实际效果却依赖于i的更新时刻,很可能把str里的一个位置跳过去,也可能导致只对i实际更新一次。这里应该把它分为两个语句:
str[i++]=’‘;
str[i++]=’‘;
例如:array[i++]=i;
如果初始时i的值是3,那吗数组元素可能被设置成3或4。
这里还有一个奇怪的例子a=(++x)+(++x)+(++x),a等于多少,大家可以试一试。
7.9避免对浮点类型做等于或不等于判断因为浮点数在计算机内部是用符号位(用±表示)、尾码(用D表示)、阶码表示(用n表示),一个浮点数等于±Dxn,它不能精确的表示所有的十进制浮点数。所以对浮点类型做等于或不等于判断存在着一定的安全隐患,用大于等于或小于等于是比较明智的替代方法。
7.0对于浮点类型可以用范围比较代替精确比较。7.慎用无符号数,尤其是无符号数与零的比较。例如无符号数小于零的条件是永远无法满足的,这种错误也是非常常见的。
7.尽量避免类型转换,从大尺寸类型到小尺寸类型转换时会出现信息丢失的现象,这要求丢失的信息一定是无用的信息。
7.3若必须用类型转换,尽量用显示类型转换显示方式更清晰、直观、可控性好,同时给其他阅读程序的人带来方便。
7.4慎用union由于union成员公用内存空间,所以容易出错,并且维护困难。
7.5慎用位操作,可能带来兼容性问题7.6慎用goto语句8语句书写规则8.用缩行显示程序的结构例如:下面的例子的格式不好。
for(n=0;n00;field[n++]=’\0’);
return(‘\n’);
应该为:
for(n=0;n00;n++)
{
field[n]=’\0’;
}
return(‘\n’);
在缩行时,用Tab键比空格键好。
)在大多数编译器中Tab键可以定义成表示多少个空格,输入时用Tab键比空格键方便。
)用Tab键对齐的文件可在不同的编译器下对齐。用空格键在某一编译器下对齐的文件,在另一个编译器下不一定能对齐。
8.不要在一行中放置多条语句,不便于阅读8.3每一行语句不要太长最好能在一屏内完全显示,语句过长时可以按逻辑划分成不同的行。
8.4用空行将代码逻辑分段8.5不要用过多的嵌套语句过多的嵌套语句使理解起来非常困难、容易出错、对于程序的维护也带来很大的不便。
8.6if语句的使用)if的左手法则,在使用判断时使用下面形式
if(TRUE==bIsChar)而不是下面形式
if(bIsChar==TRUE)避免了下面的情况发生
if(bIsChar=TRUE)
)if-elseif-else中要用{}把语句括起来,即使一句,防止以后修改出现错误。
3)条件判别成立时要用相应的处理。
4)在if…elseif语句中必须使用else分支,保证逻辑处理的完整性。
8.7switch语句的使用)case语句要按某重顺序排列,符合一定的逻辑关系。
)每个case语句要单独占一行,每个case后都要有一个break语句,若没有要特殊说明,这个特殊说明非常重要,便于他人理解、修改及确认不需要break语句。若不加特殊说明审查程序的人还意味你写错了呢!
3)要有一个default语句处理缺省的情况,便于程序的控制及程序结构的完整。
4)避免使用空switch和空case语句。
8.8.在循环过程中不要修改循环计数器例如
for(i=0;i0;i++)
{
……….
i+=;
}
若出现这样的语句意味着对循环控制能力变弱,程序出错的概率加大。
8.9变量定义应集中放置、各占一行它的好处有便于修改变量的类型、了解变量的用途、便于程序的移植等,尤其是一些编译器不支持在函数的其他地方(开始的地方除外)定义变量。
8.0花括号{}要单独占一行,这样便于对起。8.花括号{}中没有或只有一条语句时也不能省略花括号例如if(i==0)i++;
应该写成if(i==0)
{
i++;
}
原因,便于该语句的扩充,有时为了在该条件下增加一条语句忘了加{},造成比较难查出的逻辑错误。
8.不要在引用操作符(”.””-”)前后加空格若加空格打断了语义的连贯性。
8.3不要在单目操作符和其操作对象间加空格例如i++。
8.4不要在::前后加空格8.5整个程序要保持一致性一致性带来的将是更好的程序。如果程序中的格式很随意,例如对数组做循环,一会二采用下标量从下到上开始,一会儿采用从上到下开始;一会儿采用for循环一会儿采用while循环。这些变化就会使人很难看清实际上到底怎么回事。而如果相同的计算每次出现总是采用相同的方式,任何变化就预示着是经过深思熟滤,引起读程序的人注意。
8.6使用习惯用法例如,对数组赋值
i=0;
while(inSum)
{
aArray[i++]=;
}
然而习惯用法为:
for(i=0;inSum;i++)
{
nArray[i]=;
}
9函数的命名与要求在高校C/C++教材中对函数的命名为f(),g()这些命名使读者不知道到底什么含义。一个函数的名字要反映出该函数的主要功能。下面根据本人的经验谈谈函数的命名。
9.函数命名)在C语言中由于没有类的概念,对于函数来说,前面要带有其模块名。命名规则为:模块名__函数名,在书写时每个单词的第一个字母要大写,例如Str_IsChar(charc),表示的含义为在字符处理的模块中判断一个char型的变量是否为字符类型的函数。这些命名的好处是对具有同样功能的函数在以字母排序时能集中在一起,便于修改与审查等。
)函数要采用动作性的名字,即动词+名词,在书写时每个单词的第一个字母要大写,例如Sys__GetTime(),表示获取时间。
3)对返回布尔类型值的函数命名,应该清楚地反映其返回值情况。例如下面的函数命名BOOLStr__CheckChar(charc),检查c是否为字符,这样命名不如下面的好BOOLstr__IsChar(charc)。
4)在C++中对于成员函数不需要模块名,因为类名就能表示出功能,直接写函数名。
5)对于内联函数在函数的后面加大写的I,例如CmpStrI()。
9.函数的使用)函数一定要做到先定义后使用,C++必须这样做(否则编译器通不过)。C程序没有强制要求,但也应该先提供原型,再使用函数。
)函数原型的声明应集中放在一个头文件中,因为将同类/相关的(非成员)函数的原型声明集中放在一个头文件中,有利于引用和修改,因为只引用了一个熟知的头文件,修改也只在一个地方修改。
3)函数有参数时一定使用类型声明,不能使用缺省的参数类型;无参数一定要用void标注,例如intSetZero(void)比intSetZero()好,C++和C对function()的解释不同。C++认为是不带参数;C认为是带任意参数(虽然ANSIC现在已经废除这一规则,但其他标准和编译器不一定保证这一点)。在有的编译器下会出现烦人的warning。
4)禁止过程参数只有类型没有标识符。例如GetPoint(long,long)不允许。
5)函数的参数不要太多,一般一个函数的参数应该限制在5个以内。
6)函数的行数不要太多,一般在00行以内。
7)对于内置类型(如int,char,long等)参数应该用传值形式。
8)对于非内置类型参数应传递引用或指针。非内置类型指的是自定义的类、结构和联合,原因:
▲不用拷贝:非内置类型的尺寸一般大于引用和指针类型,尤其还要考虑非内置类型可能包含隐式的数据成员,比如虚函数指针等,所以拷贝代价高于传递引用或指针。
▲不用构造和析构。
▲有利于非内置类型的扩充。
▲有利于函数支持派生类。
9)显示定义返回类型。
0)若函数返回状态,尝试用枚举类型,返回枚举类型可以使编译器对返回值做合法检查(看看是不是枚举的合法成员)。
)返回指针类型的函数应该用NULL表示失败,不要用0。
)当函数返回引用或指针时,一定要用文字描述其有效期,若不特殊说明,可能在程序中引用无效的指针,修改该bug时可能带来惨重的代价。
3)嵌入汇编程序的过程(函数)必须是纯汇编程序。
4)谨慎使用abort,exit等函数。
5)过程参数中慎用省略号,不利于参数匹配。
0自定义的结构和类的命名及类的设计0.结构的命名与使用)全部用大写字母表示
例如:typedefstruct
{
longx;
longy;
}LPOINT;
)禁止在结构体中定义空域
例如:structtagPOINT
{
longx;
struct//此处是空域,禁止使用
{
LonglTemp;
LonglTemp;
};
}LPOINT;
3)在结构体定义中谨慎使用位域
0.类的命名规则与设计)C+表示类的功能名,功能名中每个开头的字母用大写。
例如CDrawMap表示画地图的类。
)对象名应是名词,对象名用来标明事物,而事物应是名词。
3)实现行为的类成员函数名应是动词或动词+名词。
4)在类中先写成员变量,再写成员函数。这样可以保证成员变量与成员函数集中放置。
5)类的成员变量应该是私有的(private),利于封装。
6)提高类内的聚合度,降低类间的偶合度。
7)尽量使类的接口少而完备。
8)不要在类声明时提供成员的函数体。
9)确保成员变量在类的整个生命周期都有效,例如
classCDrawMap
{
private:
int*m_piHeigh;
}
该指针变量应该在构造函数中申请空间,在析构函数中释放。这样能保证该指针变量在整个生命周期中有效。
0)限制类继承的层数。
)慎用友元函数,因为友元函数破坏了类的封装特性。
)保证重载函数的所有版本都有共同的目的和相似的行为。
3)重载操作符时要遵守原操作符的含义、不要创新。例如不要把“+”运算符定义为减法的功能。
4)构造函数要实现完全的初始化,避免成员变量出现随机的值。如果成员变量使用了随机的值,这很可能造成不可重现的错误(这是一种非常难捉的BUG)。
5)在析构函数里要实现成员变量(主要是指针变量)的完全清除,避免内存泄露。内存的泄露是一个非常严重的问题,无论你的资源多丰富,只要存在内存泄露,资源一定有耗尽的时候。
注释注释是一种工具,它的作用是帮助读者理解程序中的某些部分,而这些部分的意义不容易通过代码本身直接看到。注释要清晰、简洁,并且有价值。
.不要大谈明显的东西注释不是去说明明明白白的事情,比如i++能够将i值加等。
、确保所有的注释随代码及时更新注释与代码保持一致性,不要让读者去猜是代码正确或注释正确。
.3澄清情况,不要添乱注释应该在困难的地方尽量帮助读者,而不是设置障碍。
.4注释不要嵌套.5不要用/**/注释大块代码应该用宏的条件编译语句#if()……….#endif。
.6对每个引用的头文件给出行末注释例如#includeiostream.h//usecout,etc
.7对空循环体给出确认性注释例如while(str[I++]!=EOF)
{
//这是一个空循环体
}
.8给全局变量加注释说明全局变量表示的含义。例如intg_hComm;表示串口的句柄。
.9程序文件的开始部分应该有下面的注释。/////////////////////////////////////////////////////////////////////
//主要功能:字符处理
//版权信息:
//文件名:dealstr.cpp
//编写者:万方杰
//相关的头文件:dealstr.h
/////////////////////////////////////////////////////////////////////
.0函数的开始处注释函数的开始处应该有函数的功能、参数的含义、返回值及用到的全局变量等说明,例如
/////////////////////////////////////////////////////////////////////
//功能描述:计算两点之间的最短路径:
//用到的全局数据:
//GPS_CHAR*pNet:指向路网的指针
//参数说明:GPS_UNINTunStartNode出发点的节点号
//GPS_UNINTunEndNode目的点的节点号
//返回值:0找路失败找路成功
//历史记录:作者名字日期备注
//万方杰-0-0创建
//万方杰-03-7添加经由地
/////////////////////////////////////////////////////////////////////
文件的命名与分类在软件开发时,不知你碰到这样的问题没有,对一个文件几个人同时修改,最后出现版本不一致,甚至出现错误等。这里可以通过对文件的命名与分类部分的解决,如果结合版本管理软件(如微软的Sourcesafe等)效果更好,下面主要谈谈文件的命名与分类
.建议一个文件中的程序总行不超过行.文件的命名最好能反映出该文件的功能,防止文件名冲突可以通过目录或加前缀的方式来防止文件名冲突。头文件名禁止使用“‘”、“\”和“/*”等字符
.3使用统一的文件名后缀例如C的源文件后缀为.c;C++的源文件后缀为.cpp;头文件的后缀为.h;动态库文件的后缀.dll等。
.4文件段落的安排)前言:文件的功能、版权等
)包含的头文件即先#include(表示系统或语言本身提供的头文件)后#include””(”表示用户自定义的头文件”)。避免了在头文件前有可执行代码。
3)宏定义部分即#define
4)类型声明或定义
5)函数的声明
6)全局变量声明或定义
7)常量声明或定义
8)函数的实现
.5为了防止头文件的多次引用,一般用下面的格式#ifndefMYFILENAME_H_
#defineMYFILENAME_H_
//头文件的内容
#endif
注:禁止在同一个文件中有#if而没有#endif
.6引用头文件时不要使用绝对路径而要使用相对路径好处为可以把程序放在不同的目录下不需要更改程序。若使用绝对路径例如g:/../../,若机器上没有g盘,其不是你要重新修改程序。
“.”表示当前路径,例如“./log.txt”表示当前目录下的log.txt,“..”表示上一级的目录。
.7不要在头文件中定义常量或变量因为如果多个源文件引用一个头文件时会出现该变量的多次定义而出错,同时头文件的编译顺序与对应的源文件的包含顺序有关。不同的包含顺序有不同的编译顺序,所以头文件的编译顺序存在着一定的偶然性。
.8应该有一个typedef.h文件对编译器的内置类型(如int,char,long等)进行重新定义这样便于移植:例如我在开发一个嵌入式项目时,刚开始有的6位的CPU,后来改为3位的CPU,在换CPU的时候出现以下问题:对于6位的CPU来说int类型为个字节,对于3位的CPU来说int类型为4个字节,对文件操作时原来用的int类型在3位下必须该为short型。这些修改需要很大的工作量且容易出错,如果开始时进行类型定义,仅改一句语句就行即typedefintINT6;该为typedefshortINT6。
.9对于字符处理要有一个专门文件现在字符存在两种类型ANSI和UICODE形式,不同的操作系统有采用不同的方式,例如WINCE操作系统采用的是UICODE,而WINDOWXP操作系统采用的是两种类型。因此把它专门分离出来便于程序的移植。
例如:
size_tDSstrlen(constTCHAR*str)
{
#ifdef_UNICODE
returnwcslen(str);
#else
returnstrlen(str);
#endif
}
.0与硬件有关的程序必须集中防在一个或几个文件中保证在移植程序时使修改的文件数最少。
.、在C/C++嵌入的其它语言如汇编应集中放在一个或几个文件中,便于移植和修改。
.程序必须包含一个初始化文件该文件有一系列的初始化函数组成且只能包含初始化的函数。因为该文件是一个公用的,所以修改该文件时要非常慎重,以免自己的修改影响他人。
.3在C++中源文件与头文件要一一对应3排错处理3.所有的变量都要进行初始化若没有,它就可能取得某个具有随机性的值,这可能造成不可重现的错误,这种错误非常难排除。
3.防御性的程序设计在程序里增加一些代码,专门处理所有“不可能”出现的情况。
3.3检查错误的返回值一个常被忽略的防御措施是检查函数的返回值。例如判断文件打开是否成功时一定要检查其返回值。
3.4充分利用const的健壮性作为一个程序员或设计师一定要掌握const的健壮性
3.5代码审查通过相关人员的分头阅读代码,可发现大家常犯的错误、笔误或不符合规范的代码。代码审查也是一种非常有效的排错手段。
3.6在编译时打开所有的编译检查,要北京治白癜风疗效好的医院北京白癜风最佳最好的治疗方法