高质量C语言编程指南doc.docx
- 文档编号:23181343
- 上传时间:2023-05-15
- 格式:DOCX
- 页数:12
- 大小:21.58KB
高质量C语言编程指南doc.docx
《高质量C语言编程指南doc.docx》由会员分享,可在线阅读,更多相关《高质量C语言编程指南doc.docx(12页珍藏版)》请在冰豆网上搜索。
高质量C语言编程指南doc
高质量C语言编程指南
【
【规则规则1-2-1】】为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。
【
【规则规则1-2-2】】用#include格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。
【
【规则规则1-2-3】】用#include“filename.h”格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。
【
【建议建议1-2-1】】头文件中只存放“声明”而不存放“定义”在C++语法中,类的成员函数可以在声明的同时被定义,并且自动成为内联函数。
这虽然会带来书写上的方便,但却造成了风格不一致,弊大于利。
建议将成员函数的定义与声明分开,不论该函数体有多么小。
【
【建议建议1-2-2】】不提倡使用全局变量,尽量不要在头文件中出现象externintvalue这类声明。
//版权和版本声明见示例1-1,此处省略。
#ifndefGRAPHICS_H//防止graphics.h被重复引用#defineGRAPHICS_H#include//引用标准库的头文件…#include“myheader.h”//引用非标准库的头文件…voidFunction1(…);//全局函数声明…classBox//类结构声明{…};#endif示例1-2C++/C头文件的结构高质量C++/C编程指南,v1.02001Page13of1011.3定义文件的结构定义文件的结构定义文件有三部分内容:
(1)定义文件开头处的版权和版本声明(参见示例1-1)。
(2)对一些头文件的引用。
(3)程序的实现体(包括数据和代码)。
假设定义文件的名称为graphics.cpp,定义文件的结构参见示例1-3。
//版权和版本声明见示例1-1,此处省略。
#include“graphics.h”//引用头文件…//全局函数的实现体voidFunction1(…){…}//类成员函数的实现体voidBox:
:
Draw(…){…}示例1-3C++/C定义文件的结构1.4头文件的作用头文件的作用早期的编程语言如Basic、Fortran没有头文件的概念,C++/C语言的初学者虽然会用使用头文件,但常常不明其理。
这里对头文件的作用略作解释:
(1)通过头文件来调用库功能。
在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。
用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。
编译器会从库中提取相应的代码。
(2)头文件能加强类型安全检查。
如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
高质量C++/C编程指南,v1.02001Page14of1011.5目录结构目录结构如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别保存于不同的目录,以便于维护。
例如可将头文件保存于include目录,将定义文件保存于source目录(可以是多级目录)。
如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声明”。
为了加强信息隐藏,这些私有的头文件可以和定义文件存放于同一个目录。
高质量C++/C编程指南,v1.02001Page15of101第第2章章程序的版式程序的版式版式虽然不会影响程序的功能,但会影响可读性。
程序的版式追求清晰、美观,是程序风格的重要构成因素。
可以把程序的版式比喻为“书法”。
好的“书法”可让人对程序一目了然,看得兴致勃勃。
差的程序“书法”如螃蟹爬行,让人看得索然无味,更令维护者烦恼有加。
请程序员们学习程序的“书法”,弥补大学计算机教育的漏洞,实在很有必要。
2.1空行空行空行起着分隔程序段落的作用。
空行得体(不过多也不过少)将使程序的布局更加清晰。
空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。
所以不要舍不得用空行。
【
【规则规则2-1-1】】在每个类声明之后、每个函数定义结束之后都要加空行。
参见示例2-1(a)
【
【规则规则2-1-2】】在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。
参见示例2-1(b)//空行voidFunction1(…){…}//空行voidFunction2(…){…}//空行voidFunction3(…){…}//空行while(condition){statement1;//空行if(condition){statement2;}else{statement3;}//空行statement4;}示例2-1(a)函数之间的空行示例2-1(b)函数内部的空行高质量C++/C编程指南,v1.02001Page16of1012.2代码行代码行
【
【规则规则2-2-1】】一行代码只做一件事情,如只定义一个变量,或只写一条语句。
这样的代码容易阅读,并且方便于写注释。
【
【规则规则2-2-2】】if、for、while、do等语句自占一行,执行语句不得紧跟其后。
不论执行语句有多少都要加{}。
这样可以防止书写失误。
示例2-2(a)为风格良好的代码行,示例2-2(b)为风格不良的代码行。
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=”、“”这类操作符前后不加空格。
【
【建议建议2-3-1】】对于表达式比较长的for语句和if语句,为了紧凑起见可以适当地去掉一些空格,如for(i=0;i=2000)//良好的风格if(year=2000)//不良的风格if((a=b)//不要写成b-Function();示例2-3代码行内的空格高质量C++/C编程指南,v1.02001Page18of1012.4对齐对齐
【
【规则规则2-4-1】】程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。
【
【规则规则2-4-2】】{}之内的代码块在‘{’右边数格处左对齐。
示例2-4(a)为风格良好的对齐,示例2-4(b)为风格不良的对齐。
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-4(a)风格良好风格良好的对齐示例2-4(b)风格不良风格不良的对齐高质量C++/C编程指南,v1.02001Page19of1012.5长行拆分长行拆分
【
【规则规则2-5-1】】代码行最大长度宜控制在70至80个字符以内。
代码行不要过长,否则眼睛看不过来,也不便于打印。
【
【规则规则2-5-2】】长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。
拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
if((very_longer_variable1=very_longer_variable12)//类的成员函数
【
【规则规则3-1-8】】用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。
例如:
intminValue;intmaxValue;intSetValue(…);intGetValue(…);
【
【建议建议3-1-1】】尽量避免名字中出现数字编号,如Value1,Value2等,除非逻辑上的确需要编号。
这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字(因为用数字编号最省事)。
3.2简单的简单的Windows应用程序命名规则应用程序命名规则作者对“匈牙利”命名规则做了合理的简化,下述的命名规则简单易用,比较适合于Windows应用软件的开发。
【
【规则规则3-2-1】】类名和函数名用大写字母开头的单词组合而成。
高质量C++/C编程指南,v1.02001Page24of101例如:
classNode;//类名classLeafNode;//类名voidDraw(void);//函数名voidSetValue(intvalue);//函数名
【
【规则规则3-2-2】】变量和参数用小写字母开头的单词组合而成。
例如:
BOOLflag;intdrawMode;
【
【规则规则3-2-3】】常量全用大写的字母,用下划线分割单词。
例如:
constintMAX=100;constintMAX_LENGTH=100;
【
【规则规则3-2-4】】静态变量加前缀s_(表示static)。
例如:
voidInit(…){staticints_initValue;//静态变量…}
【
【规则规则3-2-5】】如果不得已需要全局变量,则使全局变量加前缀g_(表示global)。
例如:
intg_howManyPeople;//全局变量intg_howMuchMoney;//全局变量
【
【规则规则3-2-6】】类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名。
例如:
voidObject:
:
SetValue(intwidth,intheight){m_width=width;m_height=height;}
【
【规则规则3-2-7】】为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为各种标识符加上能反映软件性质的前缀。
例如三维图形标准OpenGL的所有库函数均以gl开高质量C++/C编程指南,v1.02001Page25of101头,所有常量(或宏定义)均以GL开头。
3.3简单的简单的Unix应用程序命名规则应用程序命名规则高质量C++/C编程指南,v1.02001Page26of101第第4章章表达式和基本语句表达式和基本语句读者可能怀疑:
连if、for、while、goto、switch这样简单的东西也要探讨编程风格,是不是小题大做?
我真的发觉很多程序员用隐含错误的方式写表达式和基本语句,我自己也犯过类似的错误。
表达式和语句都属于C++/C的短语结构语法。
它们看似简单,但使用时隐患比较多。
本章归纳了正确使用表达式和语句的一些规则与建议。
4.1运算符的优先级运算符的优先级C++/C语言的运算符有数十个,运算符的优先级与结合律如表4-1所示。
注意一元运算符+-*的优先级高于对应的二元运算符。
优先级运算符结合律()[]-.从左至右!
~++--(类型)sizeof+-*returnpvTo;}示例6-5复制不重叠的内存块assert不是一个仓促拼凑起来的宏。
为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。
所以assert不是函数,而是宏。
程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。
如果程序在如果程序在assert处终止了,并不是说处终止了,并不是说含有该含有该assert的函数有错误,而是调用者出了差错,的函数有错误,而是调用者出了差错,assert可以帮助我们找到发生错误的原可以帮助我们找到发生错误的原因。
因。
很少有比跟踪到程序的断言,却不知道该断言的作用更让人沮丧的事了。
你化了很多时间,不是为了排除错误,而只是为了弄清楚这个错误到底是什么。
有的时候,程序员偶尔还会设计出有错误的断言。
所以如果搞不清楚断言检查的是什么,就很难判断错误是出现在程序中,还是出现在断言中。
幸运的是这个问题很好解决,只要加上清晰的注释即可。
这本是显而易见的事情,可是很少有程序员这样做。
这好比一个人在森林里,看到树上钉着一块“危险”的大牌子。
但危险到底是什么?
树要倒?
有废井?
有野兽?
除非告诉人们“危险”是什么,否则这个警告牌难以起到积极有效的作用。
难以理解的断言常常被程序员忽略,甚至被删除。
[Maguire,p8-p30]
【
【规则规则6-5-1】】使用断言捕捉不应该发生的非法情况。
不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。
高质量C++/C编程指南,v1.02001Page42of101
【
【规则规则6-5-2】】在在函数的入口处,使用断言检查参数的有效性(合法性)。
【
【建议建议6-5-1】】在编写函数时,要进行反复的考查,并且自问:
“我打算做哪些假定?
”一旦确定了的假定,就要使用断言对假定进行检查。
【
【建议建议6-5-2】】一般教科书都鼓励程序员们进行防错设计,但要记住这种编程风格可能会隐瞒错误。
当进行防错设计时,如果“不可能发生”的事情的确发生了,则要使用断言进行报警。
6.6引用与指针的比较引用与指针的比较引用是C++中的概念,初学者容易把引用和指针混淆一起。
一下程序中,n是m的一个引用(reference),m是被引用物(referent)。
intm;intn相当于m的别名(绰号),对n的任何操作就是对m的操作。
例如有人名叫王小毛,他的绰号是“三毛”。
说“三毛”怎么怎么的,其实就是对王小毛说三道四。
所以n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。
引用的一些规则如下:
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
以下示例程序中,k被初始化为i的引用。
语句k=j并不能将k修改成为j的引用,只是把k的值改变成为6。
由于k是i的引用,所以i的值也变成了6。
inti=5;intj=6;intk=j;//k和i的值都变成了6;上面的程序看起来象在玩文字游戏,没有体现出引用的价值。
引用的主要功能是传递函数的参数和返回值。
C++语言中,函数的参数和返回值的传递方式有三种:
值传递、指针传递和引用传递。
以下是“值传递”的示例程序。
由于Func1函数体内的x是外部变量n的一份拷贝,改变x的值不会影响n,所以n的值仍然是0。
voidFunc1(intx){x=x+10;}…intn=0;Func1(n);coutFunc();//p是“野指针”}函数Test在执行语句p-Func()时,对象a已经消失,而p是指向a的,所以p就成了“野指针”。
但奇怪的是我运行这个程序时居然没有出错,这可能与编译器有关。
高质量C++/C编程指南,v1.02001Page52of1017.8有了有了malloc/free为什么还要为什么还要new/delete?
?
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。
它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。
对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。
注意new/delete不是库函数。
我们先看一看malloc/free和new/delete如何实现对象的动态内存管理,见示例7-8。
classObj{public:
Obj(void){coutInitialize();//初始化//…a-Destroy();//清除工作free(a);//释放内存}voidUseNewDelete(void){Obj*a=newObj;//申请动态内存并且初始化//…deletea;//清除并且释放内存}示例7-8用malloc/free和new/delete如何实现对象的动态内存管理类Obj的函数Initialize模拟了构造函数的功能,函数Destroy模拟了析构函数的功能。
函数UseMallocFree中,由于malloc/free不能执行构造函数与析构函数,必须调用成员函数Initialize和Destroy来完成初始化与清除工作。
函数UseNewDelete则简单得多。
高质量C++/C编程指南,v1.02001Page53of101所以我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。
由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。
既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?
这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
如果用fre
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 质量 语言 编程 指南 doc