【文档说明】新版编程规范和技巧培训课件.ppt,共(80)页,834.500 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-45664.html
以下为本文档部分文字说明:
编程规范和技巧̶编写高质量的C/C++程序精选12一定要编写高质量代码!高质量代码提高编程效率减少调试时间提高人品:代码是写给他人用的!养成好习惯从点点滴滴做起不要光看不做3程序员的境界大学计算机教育的失误:程序质量低下什么是编程老手:能
够长期稳定地编写出高质量程序的程序员什么是编程高手:能够长期稳定地编写出高难度、高质量程序的程序员4编程风格世上不存在最好的编程风格一切因需求而定团队开发讲究风格一致如果制定了大家认可的编程风格,那么所有组员都要遵守若某种编程风格比较合你的
工作,那么就采用它,不要只看不做:养成习惯!5一流代码的特性鲁棒-SolidandRobustCode简洁-MaintainableandSimpleCode高效-FastCode简短-SmallCode共享-Re-usableCode可测试-TestableCode可移植-
PortableCode一流代码6关于代码风格问题代码风格(CodingStyle)是一种习惯现在许多大公司都对员工书写代码制定了规范开发大项目时由项目管理者制定代码规范程序风格的重要构成因素程序版式命名规则函数设计原则其他表达式规则与零比较常量规则动态数组内存管理7程序
版式程序版式——程序员的书法比书法好学得多,基本不需要特别练习但是坏习惯一旦养成,就像书法一样难以改变不影响程序的功能,但影响程序的可读性追求清晰、整洁、美观、一目了然容易阅读,容易测试8程序版式不良的风格intisprime(intn){intk,i;if(n==1
)return0;k=sqrt((double)n);for(i=2;i<=k;i++){if(n%i==0)return0;}return1;}#include<stdio.h>#include<math.h>main(){inti;for(i=2;i<100;i++){i
f(isprime(i))printf("%d\t",i);}}9程序版式良好的风格intisprime(intn){intk,i;if(n==1)return0;k=(int)sqrt((doub
le)n);for(i=2;i<=k;i++){if(n%i==0)return0;}return1;}#include<stdio.h>#include<math.h>main(){inti;for(i=2;i<100;i++){if(isp
rime(i))printf("%d\t",i);}}10程序版式对齐(Alignment)与缩进(indent)——保证代码整洁、层次清晰的主要手段“{”位置的两种风格{和}独占一行,且位于同一列,与引用它们的语句
左对齐,便于检查配对情况位于同一层{和}之内的代码在{右边数格处左对齐,即同层次的代码在同层次的缩进层上一般用设置为4个空格的Tab键缩进,不用空格缩进11voidFunction(intx){…//programcode}voidFunction(intx){…//prog
ramcode}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){…//progra
mcode}如果出现嵌套的{},则使用缩进对齐,如:{…{…}…建议的风格不建议的风格12程序版式现在的许多开发环境、编辑软件都支持“自动缩进”根据用户代码的输入,智能判断应该缩进还是反缩进,替用户完成调整缩进的工作VC中有自动整理格式功能
只要选取需要的代码,按ALT+F8就能自动整理成微软的cpp文件格式13程序版式变量的对齐规则数据类型+N个TAB+变量名+[N个TAB]+=+[初始化值];例charname[20];charaddr[30];charsex='F';intage=20;floatsco
re=90;14程序版式空行——分隔程序段落的作用在每个类声明之后加空行在每个函数定义结束之后加空行在一个函数体内,相邻两组逻辑上密切相关的语句块之间加空行,语句块内不加空行//空行voidFunction1(…){…
}//空行voidFunction2(…){…}//空行voidFunction3(…){…}//空行while(condition){statement1;//空行if(condition){statement2;}else{statemen
t3;}//空行statement4;}15程序版式代码行内的空格——增强单行清晰度关键字之后加空格函数名之后不加空格赋值、算术、关系、逻辑等二元运算符前后各加一空格,但一元运算符前后一般不加空格sum=sum+term;(向后紧跟,),;向前紧跟,紧跟处不留空格
,;后留一个空格Function(x,y,z)for(initialization;condition;update)[].->前后不加空格对表达式较长的for和if语句,为了紧凑可在适当地方去掉一些空格for(i=0;i<10;i++)if
((a+b>c)&&(b+c>a)&&(c+a>b))16voidFunc1(intx,inty,intz);//良好的风格voidFunc1(intx,inty,intz);//不良的风格printf("%d%d%d",a,b,c);//良好的风格printf("%d%d%d"
,a,b,c);//不良的风格if(year>=2000)//良好的风格if(year>=2000)//不良的风格if((a>=b)&&(c<=d))//良好的风格if(a>=b&&c<=d)//不良的风格for(i=0;i<10;i++)//良好的风格for(i=0;i<1
0;i++)//不良的风格for(i=0;i<10;i++)//过多的空格x=a<b?a:b;//良好的风格x=a<b?a:b;//不良的风格int*x=&y;//良好的风格int*x=&y;//不良的风格array[5]=0;//不要写成array[5]=0;a.Function();//不
要写成a.Function();b->Function();//不要写成b->Function();程序版式17程序版式代码行一行只写一条语句,这样方便测试一行只写一个变量,这样方便写注释intwidth;//宽度inthei
ght;//高度intdepth;//深度尽可能在定义变量的同时,初始化该变量intsum=0;if、for、while、do等语句各占一行,执行语句无论有几条都用{和}将其包含在内,这样便于维护if(width<height){DoSomething();}//空行Othe
rThing();18intwidth;//宽度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;con
dition;update)dosomething();other();程序版式19程序版式长行拆分代码行不宜过长,应控制在70-~80个字符以内实在太长时要在适当位置拆分,拆分出的新行要进行适当缩进if((veryLongVar1>=veryLongVar2)&&
(veryLongVar3>=veryLongVar4)){DoSomething();}doubleFunctionName(doublevariablename1,doublevariablename2);for(very_longer_initialization;very_longe
r_condition;very_longer_update){DoSomething();}20程序版式修饰符*和&的位置有争议从语义上讲,靠近数据类型更直观,但对多个变量声明时容易引起误解int*x,y;提倡靠近变量名
int*x,y;21注释规范注释(Comments)的重要性写注释给谁看?在哪些地方写注释?怎样写注释?注释的风格写注释时的注意事项可灵活运用的一些规则22注释规范注释的重要性注释对于程序犹如眼睛对于人的重要性一样没有注释的程序对
于读者好比眼前一团漆黑,跟拿到一个可执行程序别无二致不规范的注释和好几千度的近视眼没什么区别代码本身体现不出价值开发程序的思维才能使其变得有价值这种思维的具体体现就是在于注释和规范的代码本身23注释规范写注释给谁
看?给自己看,使自己的设计思路得以连贯给继任者看,使其能够接替自己的工作24注释规范写注释的最重要的功效在于传承要站在继任者的角度写简单明了、准确易懂、防止二义性让继任者可以轻松阅读、复用、修改自己的代码让继
任者轻松辨别出哪些使自己写的,哪些是别人写的25注释规范不好的注释i=i+1;//i加1return-1;//返回-1free(p);//释放p所指的内存fclose(fin);//关闭文件/*******************************
***********//*功能描述:本函数用于实现xxx功能,目的是:*//*入口参数:参数p,表示指向结构体的指针*//*出口参数:参数xx,表示*//*返回值:返回xx值,当返回xx值时,表示*//*************************************
*****/26注释规范不好的注释不但白写,还扰乱了读者的视线/*以二进制只读方式打开文件并判断打开是否成功*/if((fin=fopen("cat.pic","rb")==NULL){puts("打开文件cat.pic失败");/*如果打开失败,则显示错误信息*/r
eturn-1;/*返回-1*/}……/*从图像的第1行到第400行循环*/for(i=0;i<400;i++)/*从图像的第1列到第400列循环*/for(j=0;j<400;j++){……/*按照公式Y=0.299*R+0.587*G+0.114*B计算灰度值*/
y=(299*r+587*g+114*b)/1000;……}……fclose(fin);/*关闭文件*/27注释规范好的注释(尤其是算法注释)是对设计思想的精确表述和清晰展现,能揭示代码背后隐藏的重要信息/*打开输入文件后判断文件长度是否符合格式要求*/if((fin=fopen(
"cat.pic","rb")==NULL){puts("打开文件cat.pic失败");return-1;}……/**下面是图像转换的算法实现。彩色图像到灰度图像的转换主要利用RGB颜色空间到*YUV颜色空间的变换公式来取得灰度值,公式为Y=0.299*R+0.587*
G+0.114*B*/for(i=0;i<400;i++)for(j=0;j<400;j++){……y=(299*r+587*g+114*b)/1000;……}……fclose(fin);28注释规范在哪些地方写注释?在重要的文件首部文件名+功能说明+[作者]+[版本]+[版权
声明]+[日期]在用户自定义函数前对函数接口进行说明函数功能+入口参数+出口参数+返回值(包括出错处理)在一些重要的语句块上方对代码的功能、原理进行解释说明在一些重要的语句行右方定义一些非通用的变量函数调用较长的、多重嵌套的语句块结束处在修改的代码行旁边加注释2
9注释规范函数的注释风格C风格/**********************************************//*功能描述:本函数用于实现xxx功能,目的是:*//*入口参数:参数xx,表示
*//*出口参数:参数xx,表示*//*返回值:返回xx值,当返回xx值时,表示*//**********************************************//*功能描述:本函数用于实现xxx功能,目的是:入口参数:参数xx,表示出口参数:参数xx,表示
返回值:返回xx值,当返回xx值时,表示*/C++风格////////////////////////////////////////////功能描述:本函数用于实现xxx功能,目的是://入口参数:参数xx,表示//出口参数:参数xx,表示//返回值:返回xx值
,当返回xx值时,表示//////////////////////////////////////////30注释规范一块语句的注释风格/**C风格*/C风格/********************************//*下面代码是用
来接收网络数据,其原理为*//*……*//********************************/////////////////////////////////////VisualC++风格////////////
////////////////////////31注释规范一行语句的注释风格/*C风格*///VisualC++风格i=j+1;//代码行右方的注释//代码行之上的注释i=j+1;例子ResetSrollInfo(g_hwndThumb);/
/初始化滚动条位置for循环{while循环{if(){……}//if结束}//while结束}//for结束32注释规范写注释时的注意事项注释不是白话文翻译,不要鹦鹉学舌注释不是教科书,不要把别人当成初学者注释不是标准库函数参考手册
注释不是越多越好,不好的注释等于垃圾不写做了什么,写想做什么边写代码边注释修改代码同时修改注释33注释规范可灵活运用的一些规则注释可长可短,但应画龙点睛,重点加在语义转折处简单的函数可以用一句话简单说明//两数交换voidSwap(int*x,i
nt*y)内部使用的函数可以简单注释,供别人使用的函数必须严格注释,特别是入口参数和出口参数34Readme的书写内容主要用来记录日期、创建者、内容等每次重大功能的添加、修改具体格式:日期—TAB—创建者—TAB—内容日期:2003
.1.21创建者:XXX内容:实例工程日期—TAB—修改的文件名—TAB—修改的功能对修改后的功能和原理的说明……日期—TAB—修改的文件名—TAB—修改的功能对修改后的功能和原理的说明35类的版式“以数据为中心”的版式private类型的数据写在前面,publi
c类型的数据写在后面关注类的内部结构“以行为为中心”的版式public类型的数据写在前面,private类型的数据写在后面关注的是类应该提供什么样的接口(或服务)提倡后者因为用户最关心的是接口36标识符命名规则按照
执行级别分为:共性规则必须执行简化规则建议采用可选规则灵活运用37标识符命名的共性规则直观可以拼读,见名知意,不必解码最好采用英文单词或其组合,切忌用汉语拼音尽量避免出现数字编号不要出现仅靠大小写区分的相似
的标识符不要出现名字完全相同的局部变量和全局变量用正确的反义词组命名具有互斥意义的变量或相反动作的函数intminValue;intmaxValue;intGetValue(…);intSetValue(…);38标识符命名的共性规则尽量与所采用的操
作系统或开发工具的风格保持一致在Linux/Unix平台习惯用“小写加下划线”function_namevariable_NameWindows风格大小写混排的单词组合而成FunctionNamevariableName39Windows应用程序命名规则Mi
crosoft公司的HungarianNotation主要思想在变量和函数名前加上前缀,用于标识变量的数据类型[限定范围的前缀]+[数据类型前缀]+[有意义的英文单词]限定范围的前缀静态变量前加前缀s_,表示static全局变量前加前缀g_,表示global类内的成员函数m_默认
情况为局部变量数据类型前缀ch字符变量前缀i整型变量前缀f实型变量前缀p指针变量前缀40Windows应用程序命名规则缺点烦琐例如inti,j,k;floatx,y,z;若采用匈牙利命名规则,则应写成intiI,iJ,ik;//前缀i表
示int类型floatfX,fY,fZ;//前缀f表示float类型41简化的Windows应用程序命名规则变量名形式小写字母开头“名词”或者“形容词+名词”如oldValue,newValue等函数名形式大写字母开头
“动词”或者“动词+名词”(动宾词组)如GetValue(),SetValue()等宏和const常量全用大写字母,并用下划线分割单词#defineARRAY_LEN10constintMAX_LEN=100;42灵活运用的命名规则限定范围的前缀与数据类型前
缀可要可不要无特殊意义的循环变量可以直接定义成i,j,k等单字母变量43表达式规则尽量简单,不要太复杂不要多用途a=i+++i+++i++;printf("%d,%d,%d",i++,i++,i++);不要与数学表达式混淆if(a<b<c)不表示if((a<b)&&(b<c))44
无需背诵的规则运算符优先级先算括号用括号确定表达式的操作顺序,避免使用默认的优先级库函数用法会查联机帮助、手册最重要45需要考虑移植性的问题不同平台,不同编译器,可能会迥然不同凡是需要字节数的地方,一律用sizeof获得46与
零比较的规则布尔变量与零比较不应写成if(flag==0)if(flag!=0)应写成if(flag)//表示flag为真if(!flag)//表示flag为假47与零比较的规则整型变量与零比较不应写成if(value)//容易误解为布尔变量if(!value)应写成
if(value==0)if(value!=0)写成如下形式能防止==误写为=if(0==value)if(0!=value)48与零比较的规则实型变量与零比较不应写成if(x==0.0)//float和double变量都有精度限制
应写成if((x>=-EPS)&&((x<=EPS))if(fabs(x)<=EPS)49与零比较的规则指针变量与零比较不应写成if(p==0)//容易误解为整型变量if(p!=0)if(p)//容易误解为布尔变量if(!p)应写成if(p==NU
LL)//强调p是指针变量if(p!=NULL)50常量规则尽量使用含义直观的常量来表示多次出现的数字或者字符串#definePI3.14159constfloatPI=3.14159;C++中用const常量完全取代宏常量需要对外公开的常量集中放在一个公共的头文件中,不需要对外公开的
常量放在定义文件的头部51常量规则怎样建立在类中恒定,且仅在类中有效的常量?#define定义的宏常量是全局的const数据成员可以吗?52常量规则classA{…constintSIZE=100;//不能在类声明中初始化const数据成员intarray[SIZE];//类的对象未
被创建时,SIZE值未知};const数据成员只能在类构造函数的初始化表中进行classA{…A(intsize);//构造函数constintSIZE;};A::A(intsize):SIZE(size){…}Aa(100);//对象a的SIZE
值为100Ab(200);//对象b的SIZE值为20053常量规则怎样建立在整个类中都恒定的常量呢?const数据成员只在某个对象生存期内是常量,而对类而言是可变的因为类可以创建多个对象不同对象的const数据成员值不同不能指望const数据成员了54常量规则
怎样建立在整个类中都恒定的常量呢?应该用类中的枚举常量来实现classA{…enum{SIZE1=100,SIZE2=200};//枚举常量intarrayA[SIZE1];intarrayB[SIZE2];};缺点:隐含数据类型是整数,其最大值有限,且不能表示浮点数55动
态数组一维动态数组int*p=NULL;p=(int*)malloc(n*sizeof(int));…p[i]//像使用一维数组一样使用…二维动态数组int*p=NULL;p=(int*)calloc(m*n,s
izeof(int));…p[i*n+j]);//像使用一维数组一样使用…56函数设计原则函数的功能要单一,不要设计多用途的函数函数的规模要小,尽量控制在50行代码以内1986年IBM在OS/360的研究结果:大多数有错误的函数都大于500行1991年对148,000行
代码的研究表明:小于143行的函数比更长的函数更容易维护57函数设计原则参数的规则参数要书写完整,不要省略参数类型和参数名没有参数时,用void填充参数个数尽量控制在5个以内参数名要恰当,顺序要合
理voidMyStrcpy(char*str1,char*str2);voidMyStrcpy(char*dstStr,char*srcStr);如果参数是指针,且仅作输入用,则应在类型前加constvo
idMyStrcpy(char*dstStr,constchar*srcStr);58函数设计原则返回值的规则不要省略返回值的类型,可声明为void确保返回值与声明的类型一致,不要依赖自动类型转换不能返回指向栈内存的指针犯了释放内存以后还继续使用的错误59函数设计原
则函数内部实现的规则在函数的入口处,使用断言assert检查参数的合法性尽量少用全局变量,确保函数的单入口和单出口,不得不用时,要严格控制对它的改写,例如,几个有关联的函数需要使用全局变量时全局变量应
和访问全局变量的函数放在单独的一个文件中,与其它文件分别编译并且将该全局变量声明为static(静态全局变量)尽量少用静态局部变量,以避免使函数具有“记忆”功能60成对编码写函数体时先写上面的大括号然后马上就写下面的大括号最后再插入函数
体内的代码动态申请内存时先分配一块内存然后马上就写释放这块内存的代码最后再在中间插入你要用这块内存做什么的代码所有变量要集中申请在函数的首部或块的首部按以上方法编程不仅能保证快速正确,而且不必等代码全部写完就可以调
试61其他不要过多假设不可能发生的情况总是会发生充分测试构造尽可能多的数据,变态的数据Codereview让别人看你的代码多看别人(高手)的代码处理错误机制返回错误信息异常处理活用断言ASSERT(
),在debug版本多用,能发现很多隐含的bugs内存管理精选62635.1内存分配方式从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。在栈上创建。在执行函
数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delet
e释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。645.2常见的内存错误内存分配未成功,却使用了它。内存分配虽然成功,但是尚未初始化就引用它。内存分配成功并且已经初始化,但操作越过了内存的边界。忘记了释放内存,造成内存泄漏
。释放了内存却继续使用它655.3习惯规则用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。避免数组或指针的下标越界,特别要当心发生“多1”或者
“少1”操作。动态内存的申请与释放必须配对,防止内存泄漏。用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。665.4free和delete的操作它们只是把指针所指的内存给释放掉,但并没有
把指针本身干掉。指针p被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p成了“野指针”。如果此时不把p设置为NULL,会让人误以为p是个合法的指针。如果程序比较长,我们有时记不住p所指的内存是
否已经被释放,在继续使用p之前,通常会用语句if(p!=NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。“野指针”示例675.5动态内存会被自动释放吗?指针消亡了,并不表示它所指的内存会被自动释放。内存被释
放了,并不表示指针会消亡或者成了NULL指针。685.6杜绝野指针“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句
对它不起作用。“野指针”的成因主要有三种:指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。指
针操作超越了变量的作用范围。这种情况让人防不胜防69705.7new/deleteMalloc/free是库函数,new/delete是运算符。光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库
函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算
符delete。71new和delete调用new所包含的动作从系统堆中申请恰当的一块内存若是对象,调用相应类的构造函数,并以刚申请的内存地址作为this参数调用delete所包含的动作若是对象,调用相应类的析构函数将该内存块返回给系统堆72new[]和delete[
]调用new[]所包含的动作从系统堆中申请可容纳n个对象外加一个整型的一块连续内存将n记录在额外的那个整型内存中调用n次构造函数初始化这块内存中的n个连续对象调用delete[]所包含的动作从new[]记录n的地方将n值找出调用n次析构函数析构这块内存中的n个连续对象将这
一整块内存(包括记录n的整型)归还系统堆73有关内存的思考题(1)74有关内存的思考题(2)75有关内存的思考题(3)76有关内存的思考题(4)77好习惯造就成功C++codingisEasy!需大量实践知错就改;经常温故而知新;坚持学习,天天向上78更多进阶
对象模型泛型编程软件工程79参考书籍SteveMaguire,WritingCleanCode(编程精粹,姜静波等译),电子工业出版社,1993.H.SutterandA.Alexandrescu.C++编程规范-101条规则
、准则与最佳实践ScottMeyers.EffectiveC++,Addison-Wesley,1992.林锐.高质量C++/C编程指南Kindsofcodingguidelines.Online.80HappyCoding