【转】中专生的C++之路!

C/C++ 专栏收录该内容
5 篇文章 0 订阅

我的文化水平不高,只是一个普通的中专生,但我热爱编程!!!!我的英文数学水平也就初中的水平!学C++一年了,走了不少弯路,主要是因为买错书,希望我的读书指南可以帮帮那些跟我一样的人。
[ 查看本指南所提及的书籍 ]
      初学嘛,最好先看看一本符合中国学生思考方式的书,这里我推荐谭浩强的 C程序设计(第二版) ,这本书据说是中国人刚开始接触编程时候的书,算是非常适合入门的一本书。记得以前在程序员杂志上看到过一遍文章,有一位中国程序界的名家<具体是谁忘记了>,他在初学的编程时,这本书上的题研究了一年,非常深入的学了一遍,而我只看了半年的时间,不算很深入,题也没怎么做,过了一遍。很多人说这本书误人子弟。。。我搞不懂为什么有人会这么说。
     接着正式开始学习C++了,先是买了一本 C++程序设计语言(特别版) ,说真的那个时候看这本书就像看天书似的,看到运算符重载的时候实在是看不下去了,那个时候都开始怀疑自己的智力了??编程真的这么难吗??后来才知道这本书是一本讲怎么用C++,也就是说是一种思想:应该怎么用它,怎么样用好。当一个人<初学编程>还不知道C++的基本语法的时候怎么能看懂这个?看了一个月实在看不懂的情况下,我看到网上有人推荐看一本叫 Accelerated C++中文版 的书,当时没有想就买下了,然后用了3个星期一口气看完了,看的很晕,当时很多人说这本书好,我至今还是认为这是一本垃圾书,这本书只对C++一部分语言特性做了介绍,讲的不够广。我觉得讲的不够全,不够广的书,还不如不看。
     下来买了 C++ Primer Plus(第四版)中文版 ,这本书真正的算是我入门的一本书,看了两个月看完了,后面的题也做了大部分,700多页也不算快。看了这本书,真是对C++的语法有了一个全面的了解,而且这本书后面的习题都有答案,非常不错,很适合初学。看了这本书,我把类、运算符重载、友元真正的搞清楚。之后看那本 C++程序设计语言(特别版) 也就能理解了。
     接下来我选择了学习数据结构,不学好这个,学出的代码效率,执行速度就非常的慢,非常没有效率,这里推荐一本 数据结构算法与应用C++语言描述 ,这本书重在动手,注重应用,初学者应该多动手,非常适合入门,当时就是因为看上这本书的多动手才选择它的。
     下面就可以学习windows程序设计了,在学VC++ C#等之前,我觉得应该先看看这本 Windows 程序设计(第5版)(上、下册) ,看完这个你会觉得VC++其实非常简单!!看完这本就可以学VC了。 Microsoft Visual C++.NET技术内幕(第6版) ,这本书有人说不适合初学者,不过买回来还是不错的。
    最后我想把我学习的经验告诉大家:我觉得编程是一项实践性很强的科学,无非就是看代码和写代码的一个过程,写代码的过程中会有很多问题,不要怕出现问题,问题出现的越多,才会学到越多的东西,而且这些出现问题的地方你会记忆的比较深。在学习的过程中会看很多书,有时候会有看不懂的时候,看不懂不免有些人会很郁闷<大部分应该是初学者>,我想下面这句话应该对大家有所帮助:“能看懂的书,仔细看,看不懂的书,硬着头皮看。很多时候看不懂的书,其实是你觉得自己看不懂所以才看不懂的。”我在没有看《 C++ Primer Plus》之前,一直都不知道C++基本语法的情况呀,看《 C++程序设计语言(特别版)》用了一个来月的时间。当时,我只有C的基础,这样没有看懂什么,后来看完了《 C++ Primer Plus(第四版)》以后,又从新开始看《 C++程序设计语言(特别版)》,这样既浪费金钱,还浪费自己的时间,所以我想告诉大家一些买书的经验,希望能帮助那些新手:好的书,到那里都会有人说好,我有个小经验,就是在排行榜找书,好的书它会常在排行榜不下,多去购书的网站看看网友的评价,有百分之九十五以上的人说这本书好,必是好书。
      最后我还想提到一本入门的书, C++大学教程(第二版) 这本书我觉得跟《 C++ Primer Plus(第四版)》定位相同的书,俱说里面内容的比后者还全,我也翻看了,很通俗易懂的一本书,后面也备有习题答案,算是比较难得的啦,初学者也可以考虑这本。

给初学编程者的忠告 作者:刘巍

我始终认为,对一个初学者来说,IT界的技术风潮是不可以追赶的,而且也没有能力去追赶。我时常看见自己的DDMM们把课本扔了,去卖些价格不菲的诸如C#, VB.Net 这样的大部头,这让我感到非常痛心。而许多搞不清指针是咋回事的BBS站友眉飞色舞的讨论C#里面可以不用指针等等则让我觉得好笑。C#就象当年的ASP一样,“忽如一夜春风来,千树万树梨花开”,结果许多学校的信息学院成了“Web 学院”。96,97级的不少大学生都去做Web 了。当然我没有任何歧视某一行业的意识。我只是觉得如果他们把追赶这些时髦技术的时间多花一点在基础的课程上应该是可以走得更远的。  
  
几个误区  
  
初学者对C#风潮的追赶其实也只是学习过程中经常遇到的几个误区之一。我将用一些实际的例子来说明这些现象,你可以按部就班的看看自己是不是属于其中的一种或者几种:  
  
认为计算机技术等于编程技术:  
  
有些人即使没有这个想法,在潜意识中也有这样的冲动。让我奇怪的是,许多信息学院的学生也有这样的念头。认为计算机专业就是编程专业,与编程无关的,或者不太相关的课程他统统都不管,极端的学生只要书上没带“编程”两个字他就不看。  
  
其实编程只是计算机技术应用过程中一种复杂性最低的劳动,这就是为什么IT业最底层的人是程序员(CODER)。计算机技术包括了多媒体,计算机网络,人工智能,模式识别,管理信息系统等等这些方面。编程工作只是在这些具体技术在理论研究或者工程实践的过程中表达算法的过程。编程的人不一定对计算机技术的了解就一定很高。而一个有趣的现象是,不少大师级的计算机技术研究者是不懂编程的。网上的炒作和现实中良好的工作待遇把编程这种劳动神秘化了。其实每一个程序员心里都明白,自己这些东西,学的时候并不比其它专业难,所以自然也不会高档到哪里去。  
  
咬文嚼字的孔已己作风:  
  
我见过一本女生的《计算机网络原理》教材,这个女生象小学生一样在书上划满了横杠杠,笔记做得满满的,打印出来一定比教材还厚。我不明白的是,象计算机网络原理这样的课程有必要做笔记?我们的应试教育的确害了不少学生,在上《原理》这一类课程的时候许多学生象学《马列原理》一样逐字背诵记忆。这乃是我见过的最愚蠢的行为。所谓《原理》,即是需要掌握它为什么这样做,学习why,而不是how(怎样做)。极端认真的学生背下以太网的网线最大长度,数据帧的长度,每个字段的意义,IP报头的格式等等,但是忘了路由的原则,忘了TCP/IP协议设计的宗旨。总之许多人花了大量的时间把书背得滚瓜烂熟却等于什么也没学。  
  
在学习编程的时候这些学生也是这样,他们确切的记得C++语法的各个细节。看完了C++教程后看《Thinking in C++》(确实是好书),《Inside C++》,《C++ reference》,this C++, that C++……,然后是网上各种各样的关于C++语法的奇闻逸事,然后发现自己又忘了C++的一些语法,最后回头继续恶补…。有个师弟就跟我说:“C++ 太难了,学了这里忘了那里,学了继承忘了模板。”我的回答道:“你不去学就容易了”。我并没有教坏他,只是告诉他,死抠C++的语法就和孔已己炫耀茴香豆的茴字有几种写法一样毫无意义。你根本不需要对的C++语法太关心,动手编程就是了,有不记得的地方一查MSDN就立马搞定。我有个结论就是,实际的开发过程中对程序语法的了解是最微不足道的知识。这是为什么我在为同学用Basic(我以前从没有学过它)写一个小程序的时候,只花了半个小时看了看语法,然后再用半个小时完成了程序,而一个小时后我又完全忘记了Basic 的所有关键字。  
  
不顾基础,盲目追赶时髦技术:  
  
终于点到题目上来了。大多数的人都希望自己的东西能够马上跑起来,变成钱。这种想法对一个已经进入职业领域的程序员或者项目经理来说是合理的,而且IT技术进步是如此的快,不跟进就是失业。但是对于初学者来说(尤其是时间充裕的大中专在校生),这种想法是另人费解的。一个并未进入到行业竞争中来的初学者最大的资本便是他有足够的时间沉下心来学习基础性的东西,学习why 而不是how。时髦的技术往往容易掌握,而且越来越容易掌握,这是商业利益的驱使,为了最大化的降低软件开发的成本。但在IT领域内的现实就是这样,越容易掌握的东西,学习的人越多,而且淘汰得越快。每一次新的技术出来,都有许多初学者跟进,这些初学者由于缺乏必要的基础而使得自己在跟进的过程中花费大量的时间,而等他学会了,这种技术也快淘汰了。基础的课程,比方数据结构,操作系统原理等等虽然不能让你立马就实现一个linux(这是许多人嘲笑理论课程无用的原因),但它们能够显著的减少你在学习新技术时学习曲线的坡度。而且对于许多关键的技术(比方Win32 SDK 程序的设计,DDK的编程)来说甚至是不可或缺的。  
  
一个活生生的例子是我和我的一个同学,在大一时我还找不到开机按纽,他已经会写些简单的汇编程序了。我把大二的所有时间花在了汇编,计算机体系结构,数据结构,操作系统原理等等这些课程的学习上,而他则开始学习HTML和VB,并追赶ASP的潮流。大三的时候我开始学习Windows 操作系统原理,学习SDK编程,时间是漫长的,这时我才能够用VC开发出象模象样的应用程序。我曾一度因为同学的程序已经能够运行而自己还在学习如何创建对话框而懊恼不已,但临到毕业才发现自己的选择是何等的正确。和我谈判的公司开出的薪水是他的两倍还多。下面有一个不很恰当的比方:假设学习VB编程需要4个月,学习基础课程和VC的程序设计需要1年。那么如果你先学VB,再来学习后者,时间不会减少,还是1年,而反过来,如果先学习后者,再来学VB,也许你只需要1个星期就能学得非常熟练。  

几个重要的基础课程  
  
如果你是学生,或者如果你有充足的时间。我建议你仔细的掌握下面的知识。我的建议是针对那些希望在IT技术上有所成就的初学者。同时我还列出了一些书目,这些书应该都还可以在书店买到。说实在的,我在读**人的文章时最大的心愿就是希望作者列出一个书单。  
  
大学英语-不要觉得好笑。我极力推荐这门课程是因为没有专业文档的阅读能力是不可想象的。中文的翻译往往在猴年马月才会出来,而现在的许多出版社干脆就直接把E文印刷上去。学习的方法是强迫自己看原版的教材,开始会看不懂,用多了自然熟练。吃得苦下得狠心绝对是任何行业都需要的品质。  
  
计算机体系结构和汇编语言-关于体系结构的书遍地都是,而且也大同小异,倒是汇编有一本非常好的书《80x86汇编语言程序设计教程》(清华大学出版社,黑色封面,杨季文著)。你需要着重学习386后保护模式的程序设计。否则你在学习现代操作系统底层的一些东西的时候会觉得是在看天书。  
  
计算机操作系统原理-我们的开发总是在特定的操作系统上进行,如果不是,只有一种可能:你在自己实现一个操作系统。无论如何,操作系统原理是必读的。这就象我们为一个芯片制作外围设备时,芯片基本的工作时序是必需了解的。这一类书也很多,我没有发现哪一本书非常出众。只是觉得在看完了这些书后如果有空就应该看看《Inside Windows 2000》(微软出版社,我看的是E文版的,中文的书名想必是Windows 2000 技术内幕之类吧)。关于学习它的必要性,ZDNET上的另一篇文章已经有过论述。  
  
数据结构和算法-这门课程能够决定一个人程序设计水平的高低,是一门核心课程。我首选的是清华版的(朱战立,刘天时)。很多人喜欢买C++版的,但我觉得没有必要。C++的语法让算法实现过程变得复杂多了,而且许多老师喜欢用模块这一东西让算法变得更复杂。倒是在学完了C版的书以后再来浏览一下C++的版的书是最好的。  
  
软件工程-这门课程是越到后来就越发现它的重要,虽然刚开始看时就象看马哲一样不知所云。我的建议是看《实用软件工程》(黄色,清华)。不要花太多的时间去记条条框框,看不懂就跳过去。在每次自己完成了一个软件设计任务(不管是练习还是工作)以后再来回顾回顾,每次都会有收获。  
  
Windows 程序设计-《北京大学出版社,Petzold著》我建议任何企图设计Windows 程序的人在学习VC以前仔细的学完它。而且前面的那本《Inside Windows 2000》也最好放到这本书的后面读。在这本书中,没有C++,没有GUI,没有控件。有的就是如何用原始的C语言来完成Windows 程序设计。在学完了它以后,你才会发现VC其实是很容易学的。千万不要在没有看完这本书以前提前学习VC,你最好碰都不要碰。我知道的许多名校甚至都已经用它作为教材进行授课。可见其重要。  
  
上面的几门课程我认为是必学的重要课程(如果你想做Windows 程序员)。  
  
对于其它的课程有这样简单的选择方法:如果你是计算机系的,请学好你所有的专业基础课。如果不是,请参照计算机系的课程表。如果你发现自己看一本书时无法看下去了,请翻到书的最后,看看它的参考文献,找到它们并学习它们,再回头看这本书。如果一本书的书名中带有“原理”两个字,你一定不要去记忆它其中的细节,你应该以一天至少50页的速度掌握其要领。尽可能多的在计算机上实践一种理论或者算法。  
  
你还可以在CSDN上阅读到许多书评。这些书评能够帮助你决定读什么样的书。  
  
日三省乎己
      
每天读的书太多,容易让人迷失方向。一定要在每天晚上想想自己学了些什么,还有些什么相关的东西需要掌握,自己对什么最感兴趣,在一本书上花的时间太长还是不够等等。同时也应该多想想未来最有可能出现的应用,这样能够让你不是追赶技术潮流而是引领技术潮流。同时,努力使用现在已经掌握的技术和理论去制作具有一定新意的东西。坚持这样做能够让你真正成为一个软件“研发者”而不仅仅是一个CODER。  
  
把最多的时间花在学习上  

这是对初学者最后的忠告。把每个星期玩SC或者CS的时间压缩到最少,不玩它们是最好的。同时,如果你的ASP技术已经能够来钱,甚至有公司请你兼职的话,这就证明你的天份能够保证你在努力的学习之后取得更好的收益,你应该去做更复杂的东西。眼光放长远一些,这无论是对谁都是适用的。  
  
相信你已经能够决定是否学习C#或者什么时候去学它了。

c语言的编程风格

第一章:缩进格式
  
  Tab是8个字符,于是缩进也是8个字符.有很多怪异的风格,他们将缩进格式定义为4个字符(设置为2个字符!)的深度,这就象试图将PI定义为3一样让人难以接受.  
  
  理由是:缩进的大小是为了清楚的定义一个块的开始和结束.特别是当你已经在计算机前面呆了20多个小时了以后,你会发现一个大的缩进格式使得你对程序的理解更容易.  
  
  现在,有一些人说,使用8个字符的缩进使得代码离右边很近,在80个字符宽度的终端屏幕上看程序很难受.回答是,但你的程序有3个以上的缩进的时候,你就应该修改你的程序.  
  
总之,8个字符的缩进使得程序易读,还有一个附加的好处,就是它能在你将程序变得嵌套层数太多的时候给你警告.这个时候,你应该修改你的程序.  
  
第二章:大符号的位置
  
  另外一个C程序编程风格的问题是对大括号的处理.同缩进大小不同,几乎没有什么理由去选择一种而不选择另外一种风格,但有一种推荐的风格,它是Kernighan和Ritchie的经典的那本书带来的,它将开始
的大括号放在一行的最后,而将结束大括号放在一行的第一位,如下所示:  
  
  if (x is true) { we do y }  
  
  然而,还有一种特殊的情况:命名函数:开始的括号是放在下一行的第一位,如下:
int function(int x) { body of function }  
  
  所有非正统的人会非难这种不一致性,但是,所有思维正常的人明白: (第一) K&R是___对___的,(第二)如果K&R不对,请参见第一条. (:-))......另外,函数也是特殊的,不一定非得一致.  
  
  需要注意的是结束的括号在它所占的那一行是空的,__除了__它跟随着同一条语句的继续符号.如"while"在do-while循环中,或者"else"在if语句中.如下:  
  
  do { body of do-loop } while (condition);  
  以及
  if (x == y) { .. } else if (x &gt; y) { ... } else { .... }
  
  理由: K&R.  
  
  另外,注意到这种大括号的放置方法减小了空行的数量,但却没有减少可读性.于是,在屏幕大小受到限制的时候,你就可以有更多的空行来写些注释了.  
  
第三章:命名系统
  
  C是一种简洁的语言,那么,命名也应该是简洁的.同MODULE-2以及ASCAL语言不同的是,C程序员不使用诸如ThisVariableIsATemporaryCounter之类的命名方式.一个C语言的程序员会将之命名为"tmp",这很容易书写,且并不是那么难以去理解.  
  
  然而,当混合类型的名字不得不出现的时候,描述性名字对全局变量来说是必要的了.调用一个名为"foo"全局的函数是很让人恼火的.全局变量(只有你必须使用的时候才使用它) ,就象全局函数一样,需要描述性的命名方式.假如你有一个函数用来计算活动用户的数量,你应该这样命名--"count_active_users()"--或另外的相近的形式,你不应命名为"cntusr()".  
  
  有一种称为Hungarian命名方式,它将函数的类型编码写入变量名中,这种方式是脑子有毛病的一种表现---编译器知道这个类型而且会去检查它,而这样只会迷惑程序员. --知道为什么Micro$oft为什么会生产这么多"臭虫"程序了把!!.  
  
  局部变量的命名应该短小精悍.假如你有一个随机的整数循环计数器,它有可能有"i",如果没有任何可能使得它能被误解的话,将其写作"loop_counter"是效率低下的.同样的,""tmp"可以是任何临时数值的函数变量.  
  
  如果你害怕混淆你的局部变量的名字,还有另外一个问题,就是称
function-growth-hormone-imbalancesyndrome.  
  
第四章:函数
  
  函数应该短小而迷人,而且它只作一件事情.它应只覆盖一到两个屏幕(80*24一屏),并且只作一件事情,而且将它做好.(这不就是UNIX的风格吗,译者注).  
  
  一个函数的最大长度和函数的复杂程度以及缩进大小成反比.于是,如果你已经写了简单但长度较长的的函数,而且你已经对不同的情况做了很多很小的事情,写一个更长一点的函数也是无所谓的.  
  
  然而,假如你要写一个很复杂的函数,而且你已经估计到假如一般人读这个函数,他可能都不知道这个函数在说些什么,这个时候,使用具有描述性名字的有帮助的函数.  
  
  另外一个需要考虑的是局部变量的数量.他们不应该超过5-10个,否则你有可能会出错.重新考虑这个函数,将他们分割成更小的函数.人的大脑通常可以很容易的记住7件不同的事情,超过这个数量会引起混乱.你知道你很聪明,但是你可能仍想去明白2周以前的做的事情.  
  
第5章:注释
  
  注释是一件很好的事情,但是过多的注释也是危险的,不要试图区解释你的代码是注释如何如何的好:你应该将代码写得更好,而不是花费大量的时间去解释那些糟糕的代码.  
  
  通常情况下,你的注释是说明你的代码做些什么,而不是怎么做的.而且,要试图避免将注释插在一个函数体里:假如这个函数确实很复杂,你需要在其中有部分的注释,你应该回到第四章看看.你可以写些简短的注释来注明或警告那些你认为特别聪明(或极其丑陋)的部分,但是你必须要避免过多.取而代之的是,将注释写在函数前,告诉别人它做些什么事情,和可能为什么要这样做.  
  
第六章:你已经深陷其中了.  
  
  不要着急.你有可能已经被告之"GUN emacs"会自动的帮你处理C的源代码格式,而且你已经看到它确实如此,但是,缺省的情况下,它的作用还是不尽如人意(实际上,他们比随便敲出来的东西还要难看- ainfinite number of monkeys typing into GNU emacs would never make a good program)  
  
  于是,你可以要么不要使用GUN emacs,要么让它使用sanervalules.使用后者,你需要将如下的语句输入到你的.emacs文件中.(defun linux-c-mode() "C mode with adjusted defaults for use with the Linux kernel."(interactive) (c-mode) (c-set-style"K&R") (setq c-basic-offset8))  
  
  这会定义一个M-x Linux-c-mode的命令.当你hacking一个模块的时候,如何你将-*- linux-c -*-输入在最开始的两行,这个模式会自动起作用.而且,你也许想加入如下
  
  (setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.〖ch〗$" . linux-c-mode) auto-mode-alist))  
  
  到你的.emacs文件中,这样的话,当你在/usr/src/linux下编辑文件的时候,它会自动切换到linux-c-mode .  
  
  但是,假如你还不能让emaces去自动处理文件的格式,不要紧张,你还有一样东西: "缩进" .  
  
  GNU的缩进格式也很死板,这就是你为什么需要加上几行命令选项.然而,这还不算太坏,因为GNU缩进格式的创造者也记得K&R的权威, (GNU没有罪,他们仅仅是在这件事情上错误的引导了人们) ,你要做的就只有输入选项"-kr -i8"(表示"K&R,缩进8个字符).  
  
  "缩进"有很多功能,特别是当它建议你重新格式你的代码的时候,你应该看看帮助.但要记住: "缩进"不是风格很差的程序的万灵丹.

C语言中操作字符串的一些函数源代码

很多人认为C语言中的难点是指针,对指针的理解直接关系到所编程序的好坏,所以,  
在这里列举了一些C编译器通常都有的标准函数的源代码,看过它们,就能对指针和字符串  
有所了解了.  
1. strlen(),计算字符串长度  
int strlen(const char string)  
{  
int i=0;  
while(string) i++;  
return i;  
}  
2. strcpy(), 字符串拷贝.  
char *strcpy(char *destination, const char *source)  
{  
while(*destinaton++=*source++);  
return (destination-1);  
}  
3. strcat(), 字符串的连接.  
char *strcat(char *target,const char *source)  
{  
char *original=target;  
while(*target) target++; // Find the end of the string  
while(*target++=*source++);  
return(original);  
}  
4. streql(), 判断两个字符串是否相等.  
int streql(char *str1,char *str2)  
{  
while((*str1==*str2)&&(*str1))  
{  
str1++;  
str2++;  
}  
return((*str1==NULL)[$&(*str2==NULL))]  
}  
5. strchr(), 在字符串中查找某个字符.  
char *strchr(const char *string,int letter)  
{  
while((*string!=letter)&(*string))  
string++;  
return (string);  
}  
6. chrcnt(), 计算某个字符在字符串中出现的次数.  
int chrcnt(const char *string,int letter)  
{  
int count=0;  
while(*string)  
if(*string==letter)count++;  
return count;  
}  
7. strcmp(), 判断两个字符串是否相等.  
int strcmp(const char *str1,const char *str2)  
{  
while((*str1==*str2)&&(*str1))  
{  
str1++;  
str2++;  
}  
if((*str1==*str2)&&(!*str1)) //Same strings  
return o;  
else if((*str1)&&(!*str2)) //Same but str1 longer  
return -1;  
else if((*str2)&&(!*str1)) //Same but str2 longer  
else  
return((*str1&gt;*str2)?-1:1);  
}


函数指针和函数引用的区别

函数指针是C++最大的优点之一。和使用普通指针相比,高级程序员只要有可能都更愿意使用引用,因为引用更容易处理一些。然而,当处理函数时,函数引用对比函数指针就未必有这个优势了。现有的代码很少使用函数引用。
  
在本文中,我们将向你介绍如何函数指针、如何使用函数引用以及分别在什么情况下使用它们。
  
下面是函数指针的一些例子:
  
#include &lt;iostream&gt;
  
void print(inti)
{ std::cout &lt;&lt; i &lt;&lt; std::endl; }
  
void print_2(inti)
{ std::cout &lt;&lt; i &lt;&lt; std::endl; }
  
void multiply( int & nDest, intnBy)
{ nDest *= nBy; }
  
void print_something()
{ std::cout &lt;&lt; "something" &lt;&lt; std::endl; }
  
int return_1()
{ return 1; }
  
int main()
{   
     void (*func_1)(int);
     func_1 = [$print]
     func_1( 1);
     //或者,我们这一这样调用它
     //(由于它是一个指针,所以它可以重引用)
     (*func_1)( 1);
     func_1 = [$print_2]
     func_1( 1);
      
     void (*func_2)(int) = [$print_2]
     func_2( 1);
  
     void (*func_3)(int[$, int) = &multiply]
     inti = 1;
     std::cout &lt;&lt; "[before] i=" &lt;&lt; i &lt;&lt; std::endl;
     (*func_3)( i, 10);
     std::cout &lt;&lt; "[after]  i=" &lt;&lt; i &lt;&lt; std::endl;
      
     void (*func_4)();
     func_4 = [$print_something]
     func_4();
      
     int (*func_5)();
     //注意:有些编译器可以让你写成  
     // “func_5 = return_1;”(即忽略了‘&’);
     //但是,我们不推荐这种写法;
     //下面的用法就揭示了这样一个事实:  
     //‘func_5’是一个指针,如果你忽略了‘&’  
     // 代码的含义就不够清晰
     func_5 = [$return_1]
     std::cout &lt;&lt; (*func_5)() &lt;&lt; std::endl;
      
     std::cin.get();
     return 0;
}  
      
   
  
下面是关于函数引用的一些例子:
  
// print, print_2, multiply, print_something, return_1
//等函数和上面的相同。
  
int main()
{   
     // 错误:未初始化引用!  
     // void ([$func_1)(int)]
      
     void ([$func_1)(int) = print]
     func_1( 1);
  
     //错误:不能向引用赋值!  
     // func_1 = [$print_2]
  
     void ([$func_2)(int) = print_2]
     func_2( 1);
      
     void ([$func_3)(int&, int) = multiply]
     inti = 1;
     std::cout &lt;&lt; "[before] i=" &lt;&lt; i &lt;&lt; std::endl;
     func_3( i, 10);
     std::cout &lt;&lt; "[after]  i=" &lt;&lt; i &lt;&lt; std::endl;
      
     void ([$func_4)() = print_something]
     func_4();
      
     int ([$func_5)() = return_1]
     std::cout &lt;&lt; func_5() &lt;&lt; std::endl;
      
     std::cin.get();
     return 0;
}  
  
由于现有的代码通常使用函数指针,所以我们建议你遵循这个惯例。我们更喜欢用函数指针的另一个原因是它的连贯性,除了指向函数的指针外,你还可以有指向成员函数的指针。
  
函数引用也可以作为函数参数来使用,它意味着值不变。例如:
  
#include &lt;iostream&gt;
  
void print(inti)
{ std::cout &lt;&lt; i &lt;&lt; std::endl; }
  
void print_2(inti)
{ std::cout &lt;&lt; i * 2 &lt;&lt; std::endl; }
  
void call_func(void (&func)(int))
{
     //下面这一行就会导致错误发生:
     //(如果该函数当作指针传递,
     // 这样也可能正确)
     //func = print_2;
      
     func( 1);
     func( 2);
     func( 3);
}
  
int main()
{   
     call_func( print);
     call_func( print_2);
     std::cin.get();
     return 0;
}  
  
创建一个指向函数的const型指针是困难的;如果用函数引用,那么它天然就有这个特性。

控制C++的内存分配

在嵌入式系统中使用C++的一个常见问题是内存分配,即对new 和 delete 操作符的失控。
  
具有讽刺意味的是,问题的根源却是C++对内存的管理非常的容易而且安全。具体地说,当一个对象被消除时,它的析构函数能够安全的释放所分配的内存。   
  
这当然是个好事情,但是这种使用的简单性使得程序员们过度使用new 和 delete,而不注意在嵌入式C++环境中的因果关系。并且,在嵌入式系统中,由于内存的限制,频繁的动态分配不定大小的内存会引起很大的问题以及堆破碎的风险。
  
作为忠告,保守的使用内存分配是嵌入式环境中的第一原则。
  
但当你必须要使用new 和delete时,你不得不控制C++中的内存分配。你需要用一个全局的new 和delete来代替系统的内存分配符,并且一个类一个类的重载new 和delete。
  
一个防止堆破碎的通用方法是从不同固定大小的内存持中分配不同类型的对象。对每个类重载new 和delete就提供了这样的控制。
  
重载全局的new 和delete 操作符
可以很容易地重载new 和 delete 操作符,如下所示:
  
void * operator new(size_t size)
{
     void *p = malloc(size);
     return (p);
}
void operator delete(void *p);
{
     free(p);
}  
  
这段代码可以代替默认的操作符来满足内存分配的请求。出于解释C++的目的,我们也可以直接调用malloc() 和free()。
  
为单个类的的new 和delete 操作符重载
   
也可以对单个类的new 和 delete 操作符重载。这是你能灵活的控制对象的内存分配。
  
class TestClass {
     public:
         void * operator new(size_t size);
         void operator delete(void *p);
         // .. other members here ...
};
  
void *TestClass::operator new(size_t size)
{
     void *p = malloc(size);  // Replace this with alternative allocator
     return (p);
}
void TestClass::operator delete(void *p)
{
     free(p);  // Replace this with alternative de-allocator
}  
  
所有TestClass 对象的内存分配都采用这段代码。更进一步,任何从TestClass 继承的类也都采用这一方式,除非它自己也重载了new 和 delete 操作符。通过重载new 和 delete 操作符的方法,你可以自由地采用不同的分配策略,从不同的内存池中分配不同的类对象。
  
为单个的类重载 new[ ] 和 delete[ ]  
必须小心对象数组的分配。你可能希望调用到被你重载过的new 和 delete 操作符,但并不如此。内存的请求被定向到全局的new[ ]和delete[ ] 操作符,而这些内存来自于系统堆。
  
C++将对象数组的内存分配作为一个单独的操作,而不同于单个对象的内存分配。为了改变这种方式,你同样需要重载new[ ] 和 delete[ ]操作符。
  
class TestClass {
     public:
         void * operator new[ ](size_t size);
         void operator delete[ ](void *p);
         // .. other members here ..
};
void *TestClass::operator new[ ](size_t size)
{
     void *p = malloc(size);
     return (p);
}
void TestClass::operator delete[ ](void *p)
{
     free(p);
}
int main(void)
{
     TestClass *p = new TestClass[10];
      
     // ... etc ...
      
     delete[ ] p;
}  
  
但是注意:对于多数C++的实现,new[]操作符中的个数参数是数组的大小加上额外的存储对象数目的一些字节。在你的内存分配机制重要考虑的这一点。你应该尽量避免分配对象数组,从而使你的内存分配策略简单。

了解三种C++存储方式

C++有三种存储方式:自动存储方式,静态存储方式和自由存储方式。每一种存储方式都有不同的对象初始化的方法和生存空间。在下面的段落中我们将阐述这三种存储方式的不同之处,并向大家展示怎样有效而安全地使用它们。
  
自动存储方式
   
通常,我们并不把局部对象定义为静态的或者外部的,而是将它定义为自动的和寄存器的。函数的自变量都是自动存储,这种存储方式被称作栈存储。下面的例子包括了多种声明对象的方式、自动存储方式的各种形式。
  
//s' storage type s is determined by the caller
void f(const std::string [$ s)]
  
//arguments passed by value are automatic
void g(register int n);
  
int main()
{
  int n; // automatic because local, non-static, non-extern
  register inti;  // register implies automatic
  auto double d;  // auto implies automatic
  g(n); //passing a copy of n; the copy is automatic
  std::string s;
  f(std::string temp()); // a temp object is also automatic
}  
  
自动对象通常被建立在一个函数或者一个块中,当函数或块结束时,自动对象就被立即销毁。因而,当它每次进入一个函数或块的时候,自动对象将会创建一个全新的设置,自动变量和无类对象的缺省值是不定的。
   
静态存储方式
   
全局对象、一个类的静态数据成员和函数的静态变量都属于静态存储的范畴。一个静态对象的内存地址在整个程序运行的过程中是不变的。在一个程序的生存空间内,每个静态对象仅被构造一次。
  
静态数据的缺省值被初始化为二进制零,静态对象随着非无效构造函数(构造函数由编译器或者C++执行)紧接着被初始化。下面的例子展示了怎样静态存储对象。
  
int num; //global variables have static storage
static int sum; //so do static objects declared globally
intfunc()
{
   static int calls; //initialized to 0 by default
   return ++calls;
}
  
class C
{
private:
   static bool b;
};
  
namespace NS
{
   std::stringstr; //str has static storage
}  
   
自由存储方式
   
自由存储,也被称为堆存储(在C里)或者动态存储,它包括在程序代码中使new来产生所需要的对象和变量。对象和变量将不断的分配存储空间,直到调用删除操作将它们释放。
  
调用删除程序失败将会引起内存不足,调用构析函数失败的结果则是无法预料的,与自动和静态的对象相比,自由存储对象的地址在运行的时候已经被确定。下面的例子展示了自动存储对象的过程。
  
int *p = new in  t;
char *s = new char[1024];
Shape *ps=new Triangle;
//s' storage type s is determined by the caller
void f(const std::string [$ s)]
std::string *pstr=new std::string
f(pstr);
delete p;
delete[] s; //s is an array
delete  ps; //invokes the destructor
delete pstr; //ditto

C++学习之路--构造函数与析构函数

类的构造函数是在生成类的对象时自动调用的,它用来初始化对象的数据成员。类的析构函数是以类名前加上“~”为函数名的函数,它在删除对象时自动调用,即程序执行离开初始化类对象的范围时调用。析构函数本身并不实际删除对象,而是进行系统放弃内存之前的清理工作,使内存可以用于保存新的对象。
   构造函数与析构函数是自动调用的,他们的调用顺序取决于执行过程进入和离开对象的顺序。我们来举个具体例子,来说明他们的调用顺序。
  
//create.h
#ifndef CREATE_H
#define CREATE_H
  
class CreateAndDestroy{
public:
    CreateAndDestroy(int); //构造函数
    ~CreateAndDestroy(); //析构函数
private:
    int data;
};
  
#endif
  
//ceate.cpp
#include&lt;iostream.h&gt;
#include”create.h”
  
CreateAndDestroy::CreateAndDestroy(int value)
{
    data = value;
    cout&lt;&lt;”对象 ”&lt;&lt;data&lt;&lt;” 建立”;
}
  
CreateAndDestroy::~CreateAndDestroy()
{
    cout&lt;&lt;”Object “&lt;&lt;data&lt;&lt;” destructor”&lt;&lt;endl;
}
  
//create1.h
#include&lt;iostream.h&gt;
#include”create.h”
  
void create(void);
  
CreateAndDestroy first(1);//全局对象
  
int main()
{
    cout&lt;&lt;”   (全局对象在main之前建立)”&lt;&lt;endl;
    CreateAndDestroy second(2);    //局部对象
    cout&lt;&lt;”   (局部自动对象在main函数内部建立)&lt;&lt;endl;
  
    static CreateAndDestroy third(3);  //局部对象
    cout&lt;&lt;”   (局部静态对象在main中建立)“&lt;&lt;endl;
  
    create();  //调用create()建立对象
  
    CreateAndDestroy fourth(4);   //局部对象
cout&lt;&lt;”   (局部自动对象建立)”&lt;&lt;endl;
return 0;
}         
  
void create(void)
{
    CreateAndDestroy fifth(5);
    cout&lt;&lt;”   (局部自动对象在create()中建立)”&lt;&lt;endl;
  
    static CreateAndDestroy sixth(6);
    cout&lt;&lt;”   (局部静态对象在create()中建立)”&lt;&lt;endl;
     
    CreateAndDestroy seventh(7);
    cout&lt;&lt;”   (局部自动对象在create()中建立)”&lt;&lt;endl;
}
  
对象fourth和second的析构函数在到达main结尾时一次调用。由于third是static局部对象,因此到程序结束时才退出,在程序终止时删除所有**对象之后和调用first的析构函数之前调用对象third的析构函数。函数create声明三个对象。对象fifth和seventh是局部自动对象,对象sixth是static局部对象。对象seventh和fifth的析构函数在到达create结尾时自动调用。由于对象sixth是static局部对象,因此到程序结束时才退出。sixth的析构函数在程序终止时删除所有**对象之后和调用third和first的新构函数之前调用。

C++未眠夜----我学习C++的心路历程

曾经因为自己的一些帖子在网上引起了大家对C++学习和使用的讨论,再回想起自己接触C++三年多,一年前鼓足勇气重新开始再次学习的情景,现在的我心中多了几份感慨,更多的是觉得自己学习的过程颇具普遍性,几次想把自己的一些心得写下来,对自己算是个总结和回顾,对别人即使谈不上帮助但相信也能算是个学习C++的案例分析吧。但开始几次提笔总是过于追求完美,想把所有的东西都写下来,但几次提笔和几次放弃后,我终于打消了“完美”的这个想法,等一下还要和自己女朋友打电话,就这一个小时内,能说多少算多少吧,毕竟我是说出来写下来了。
  
接触C++是在99年,那个时候自己已经有一些C语言的基础了,刚开始会用的关键字比起C来说也许只多了Class吧,那个时候在学校,也学了VC++,写了几个很简单的游戏程序,然后就因为很多琐事没有再在C++上有更多的深入,这也是我现在非常遗憾的事情,也许那个时候我能够投入进去,现在应该是能颇有小成了。
  
02年开始了一个新的里程,我自己有充足的来支配时间。就在那一年的暑假,开始了新的C++学习,我知道我以前的基础对于学习C++来说,几乎不会有什么更大的帮助,所以我选择了重新开始,从IF….ELSE这样的语法开始。选择的第一本书是好友友情赠送的《C++ PRIMER》,当时我只是看了不到三天,我就没有继续看了,我知道这本书非常不错,但是肯定不适合当时的我,因为从一开始就有过多的东西给我困惑,类,模板,重载……,这样东西几乎让我放弃学习C++,因为我那个时候觉得自己C还是有一定功力的,就把C搞透对我来说那个时候也是个不错的选择,但毕竟C的内涵无法让我有更多的激情,重复的东西我向来就不喜欢。然后我开始考虑++这个两种语言唯一的标示区别到底蕴涵着什么?同时我开始考虑到底程序设计语言是什么?随后我在图书馆借了本书《程序设计语言的概念和实现》,无论别人如何看待这本书但它却让我明白了很多,因为这本书最大的特点是抽象出了现代程序设计语言的各种语义和其中蕴涵的思想,让我对语言本身这个概念有了更深刻的认识,让我建立去了各种语言自身的不同的语法都代表实现一种对于所有语言都共有的语义,而一个富有逻辑的语义系列却反映出了更一般的程序设计思想。在有了这个概念后,我接触到了《C++设计与演化》这本书,在学习C++过程中这本书对我的帮助是最大的,这本书让我明白了C++的设计理念和各种语言设施所代表的实现语义,既各种纷繁的C++语言特性都是为了实现某种功能并且在不违反C++设计哲学基础上建立起来的。这样的对于C++的宏观基础,让我在以后的学习日子里受益非浅,而《C++设计与演化》这本书我也同时具有中英两种版本,每当我对很多关于C++特性困惑的时候我都会把英文版(随便炫耀一下,这本书上有B.S的亲笔签名:))拿出来,再好好的看看。就象前阵子对于C++的学习,可谓讨论颇多,我自己也再次把这本书拿出来,评价自己所持有的观点是否正确。从此我认为自己算是走入了学习C++正确道路。
  
10月B.S来到中国,那个时候的我对于C++可以说只是个完全初学者(呵呵,给大家举个例子,那个时候我在杭州碰到了cber,他开始告诉我他的网名,我却说你的姓名是什么?后来他写下他的名字和联系方式我却还不知道我对面站着的是个C++顶尖高手,幸亏那个时候的我还和他合影了一张相片,这算是我比较明智的地方吧,至少没有留下遗憾。)我虽然是个初学者,但我不认为我应该错过这次和大师亲密接触的机会(尤其是在反复拜读过他的书和大部分论文后),自费来到杭州(其实也花了不少那里同学的钱:))听了B.S在浙大的报告,我很早就去了,坐在第一排,整个报告会也是我第一个问问题,回想那个时候我的问题多少显的幼稚,但我告诉自己无论自己现在怎么样,这样的机会一辈子也许不会有了,所以我始终保持了十分积极,B.S的答复十分详细以至与我到后面就基本处于假听的状态:》但B.S的大师风范却让我颠倒,从此对于C++的学习我也不再功利。
  
学习就是积累,期间我看过《C++编程思想》,又对C++有了更感性的了解,三个月后再次拿出《C++ PIRMER》,这个时候我已经能非常明白LIPPMAN这本经典大作的巧妙用心了,它绝对不是给初学者看的,但对于入门后完全的学习和理解C++效果却十分明显,从书的一开始就直接进入C++的主题,前面五章都用类设计一个数据结构,让读者完全明白了各种用户定义类型所代表的抽象能力,然后直接进入第六章标准库中的容器类,这样的设计让读者十分清楚的建立容器和类这两个C++中十分重要的概念,之后的学习自然是非常富有满足感。以前学习编程的经验告诉我,写程序才是学习的中心,所以并且我把《C++ PRIMER》中的大部分代码都调试通过了,样例的代码连贯性也是这本书的最大的特点,而另外一大特点就是代码没有一个是可以直接通过的,都需要自己的调试,在调试这样的代码过程中更加深了我多C++的认识。不到两个月的时间就把《C++ PRIMER》初看了一遍,加之在CSDN上面和大家的交流,感觉已经建立起了对C++的完整的基本认识。
  
没有看过《C++程序设计语言》,算是学过C++吗?在我眼里,如果连语言的创作者的书都没有读过,如何去理解这本语言?去年12月我花了整整一个月把这本书好好的看了两遍,唯一的感觉是这本书才是真正有资格称为《C++编程思想》的。书和《C++ PRIMER》是完全的不同风格,后者告诉你的是完整C++的语法和其支持的语义。而前者是真正告诉你如何去用C++思考问题和编写符合C++设计理念的代码。你能明白C++的设计理念极其理念下的程序设计思路和代码编写规范。到今年元旦,感觉自己比起当初已经是进步不小,所以没有继续看书,而是在寒假看了几个大点规模的C++程序源代码。
  
     之后,我也读了一些C++大家的作品,在这里需要提出来的是《C++标准程序库》和《C++沉思录》,前者写作方式通俗易懂,但全书内容却十分丰富,对于学习标准库可以说是最佳表现的作品。而后者,阅读感觉和《C++程序设计语言》一样,思想性非常强,读这样的书很累,脑子必须一直思考问题,思考作者里面提出的问题和他提出的解决方式。这本书最大的特点是非常直接的把C++语言的核心暴露出来-----三种抽象模型和极其语言设施本身对这三种抽象模型的支持。而《C++沉思录》给我的更深层思考是什么才是运用C++最合理的方式,这也是后来我发帖说明自己对于C++的学习和使用的一些见解的原始思想来源。
  
     再后来,自己慢慢的用C++实现了一些小程序,有的是书本上的,有的是自己想的,写程序和调试的过程给我的感觉就是烦恼和满足的交替过程也许就是这样的反复过程才是程序员追求的。文章至此,多数记载了自己的历程,对很多看到这里的读者来说相信并没有什么帮助,下面我非常直接的说出自己的学习C++感受,从前面我的经历大家都可以看的出来我不是什么高手,但我保证我下面写的东西对于初学C++的是十分有帮助的,毕竟我刚刚走过那一段时的灰暗日子。
  
学习C++重在理解其各种语言设施所代表的语义,以及C++所能表示的语义所代表的设计思想。首先从宏观上入手,你需要明白的是C++是程序设计语言的本质。在此我把C++最重要的性质写下来:C++是一门静态类型检查,基于C内存模式,支持四种基本程序设计范型的语言。注意,这里说明了三个本质特性,静态说明了语言的类型检查性质,基于C内存模式告诉我们在C++中所有与内存有关的操作都需要程序员自己来负责,这样就带来了很多设计程序时的需要注意的地方,而理解支持的四种基本范型却是理解各种语言设施的基础。然后记住C++的最大的一点设计哲学,也是其贯穿应用C++的一条本质,我引用《C++ PRIMER》中文版前言内的一句话表示--“C++的基础是各种设施,它们让用户能够通过定义新的数据类型来扩展语言本身,这些新类型可以与内置类型一样的使用方式(如何理解这句话是关键,我的理解是:用户定义类型可以在任何使用内置类型的时候做为其替代,再具体点说就是用户定义类型可以象内置类型那样可以被声明,其对象可以被初始化,可以相互复制,可以象内置类型对象那样和很多操作符一起使用,可以被作为参数传递给函数,可以作为函数的返回值,可以作为容器的操作对象,用户定义类型可以和内置类型一样作为模板的参数),掌握这些设施(哪些设施?就是让用户定义类型可以和内置类型一样方便使用的设施,包括什么?构造,拷贝,解构函数,操作副重载.....)的第一步就是理解基本语言(什么是基本语言?就是C++中better c部分)。
  
在此,我还想提出一点十分重要的概念:一门语言的最本质的东西就是其类型系统,任何语言都有自己的独特的类型系统,学习C++就是学习他的类型系统。所以首先需要彻底明白什么是数据类型这个概念。这样你的思路就清楚了,你有选择,从每一种C++支持的设计范型入手学习。
  
结构化设计-----也就是C++中的better c部分,你需要在C的基础上学会函数重载和引用这两个最重要的语言新特心性。
  
基于对象----你需要建立C++最强大的概念--类,也就是用户定义类型,这其实就是数据结构里面接触的抽象数据类型的概念,以及合理构造使用类的一系列语言措施。并始终记住,在C++进行设计时用类来表示我们需要表示的概念。类也是C++抽象机制里面最核心的概念。
  
面向对象----在这里你需要知道继承和多态这样的OO概念,以及句柄类这样常见的设计技术,这里你需要明白你开始进入了对接口编程的阶段。同时你要学会OO思想,C++的一些设施只是为了把OO思想在语言所支持的语义中充分的表示出来。但是在这里,同时你需要明白的C++能表示的语义是受到其当初语言设计原则限制的(什么原则?静态类型检查,不为不需要的东西付出代价,选择权在程序员手上语言本身不去强迫程序员的做出选择),也就是说C++所能表示的OO思想可以称为具有C++特色的OO思想了。这样的OO思想和JAVA所代表的纯OO哪个更好?没有定论,因为他们本身的设计理念就不一样。
  
  
泛型设计----这里你需要明白最重要的一点是C++的temlate机制无论其开始建立的初衷是什么,现在他所代表的真实含义是:编译时类型推导,也就是编译时多态,由此明白了泛型的核心---把各种类型当作一种抽象条件的具体化,C++有措施可以定义抽象条件吗?没有,所以这些条件只能定义在我们自己的心中,但我们可以把具体化的类型当作一种抽象条件实现在template机制中去,从而使tempalte成为比类型推导机制更进一步的东西,那就是抽象条件的推导。也就是说,我们不再把template接受的参数看成是类型,而是把template看成是一种接受抽象条件的机制,无论其类型如何,只要这种类型满足tempalte所接受的抽象条件就可以作为参数传递进去,这样就代表了一种泛型的思想-----类型不再是根本,抽象条件才是,类型不过是抽象条件的外衣和在C++中的实现方式而已。在结合基于对象和泛型设计这两种抽象设计模式的技术上诞生的工业产品就是---STL。
  
  
最后再说点**的想法,C++作为一门支持多种设计模型的计算机程序设计语言,其蕴涵思想可谓博大精深,熟练掌握已属不易,精通更是难如登天,但就象C++开始设计的那样,你对C++还不了解的地方并不妨碍你使用C++和用C++在你力所能及的范围内作出合理的设计。但我们应该明白,到一定程度的时候我们应该跳出去,站在一个更高的地方去重新审视C++,重新审视自己对于程序语言,对于设计思想,甚至与对于软件本质的认识是否在不断的进步?对于C++的全心投入不能成为忽略**的技术掌握的借口,总之,面对纷繁而来的新事物我们要有自己的判断,过于浮华是我们所不耻的,但在成长的道路也决不能故步自封,做井底之蛙。

C++编程技巧

1:  
C++程序应以简单和直接的方式编写,称为KIS(“keep it simple”,保持简单),不要使用不常用的方法任意的扩大程序
2:  
每段程序应以注释语句开头,描述该程序的用途!  
  
比如:  
//OUR FIRST PROGRAM "Hello"  
  
#include&lt;iostream.h&gt;  
  
int main()  
{  
cout&lt;&lt;"Hello! C++";  
  
return 0;  
}
3:
可以一行只声明一个变量,这样可以在每个声明后面插入注释语句。
4:
在每个“,”后面加上空格,使程序更易读。
比如:
int a, b, c; 比 int a,b,c;易读。
5:
选择有意义的变量名能使程序更清楚。
6:
避免使用下划线和双下划线开头的标识符,一位C++内部使用这类名称。
7:
可执行语句与声明之间最好留一行空格,这样能使声明更明显,程序更清晰。
8:
二元运算符两边放上空格,能使运算符更明显,程序更易读。
9:
可以在表达式中加上多余的括号,使代码更清晰,这些括号成为冗余括号。
10:
if语句的缩排可以提高程序的可读性,突出结构体。
比如:
if(n1 &lt; n2)
n1 = n2;
  
if(n1 &lt; n3)
    n1 = n3;
11:
程序中一行只放一条语句。
12:
长语句可以分成多行。如果一条语句可以分成多行,可以在分隔列表的逗号或长表达式的运算符后面断行。
比如:
int num[10] = {0, 1, 2, 3, 4,
              5, 6, 7, 8, 9};
  
total = num[0] + num[1] + num[2] + num[3] + num[4] +
        num[5] + num[6] + num[7] + num[8] + num[9];
13:
如果无法确定求值表达式中的求值顺序,可以用括号强制顺序。
14:
在整个程序中,坚持用合理的缩排规则能大大提高程序可读性,建议用三个空格的缩排量。
15:
一定要初始化计数器和总和变量。
16:
不要比较浮点数值的相等和不等性,而要测试差值绝对值是否小于指定的值。
17:
在声明中进行变量初始化可以避免数据未初始问题。
18:
一元运算符及其操作数之间不能插入空格。
19:
用整数值控制计数循环。
20:
缩排每个控制结构体中的语句。
比如:
for(int i = 0; i&lt;10; i++)
    total = total + num;
21:
在每个控制结构前后加上空行,使其在程序中一目了然。
22:
嵌套太多会使程序难以理解。一般来说,缩排不易超过3层。
23:
应在switch结构中提供default case。
24:
尽管省略返回值类型默认为int,但最好显式指定返类型。
25:
向函数传递的参数和函数定义中的对应参数可以同名,但最好不要同名,以免引起歧异。
26:
选择有意义的函数名和有意义的参数名,避免大量使用注释语句。
27:
避免在内层和外层块中使用相同的标识符,以免外层块范围的变量被隐藏。
28:
inline(内联函数)限定符只用于经常使用的小函数。
29:
将每个数组的长度定义为常量变量而不是常量,能使程序更清晰,并使其伸缩性更强。
例如:
const int a = 5;
int num[a]={0, 1, 2, 3, 4};
30:
努力保证程序清晰性,有时甚至可以为此而牺牲性能。
31:
在函数原型中包括参数的名称可以使程序清晰,但编译器忽略这个名称。
32:
习惯上将双下标数组的第一个下标表示行,第二个下标表示列。
33:
在函数原型的参数表中,多下标数组的第一个下标长度是不需要,但随后的所有下标长度多时必须的。
34:
尽管不是必须的,但在指针变量中加上ptr能更清楚地表示这些变量是指针。
比如:
int *countptr;
35:
向函数传递数组时,同时传递数组长度(而不是在函数中建立数组长度信息),能使函数更加一般化,以便在许多程序中复用。
比如:
const int size = 5;
int num[size] = {0, 1, 2, 3, 4};
   
print(num, size);
36:
指针算法只有在对数组进行时才有意义。
37:
操作数组时用数组符号而不用指针符号使程序更清晰。
38:
在字符数组中存放字符串时,一定保证能存下要存的最大字符串,否则越界字符会改写数组后面内存地址中存放的数据。
39:
每个成员访问说明符只在类定义中使用一次。则样可以增加清晰性与可读性,将public成员放在前面,便于查找。
比如:
class Time{
public:
    Time();
    void setTime(int, int, int);
    void printTime();
private:
    int hour;
    int minute;
    int second;
}
40:
尽管public和private标号可以重复和混合,但最好先将所有public成员列成一组,然后将private成员列成一组,这样可以使客户集中注意类的public接口,而不是类的实现方法。
41:
一般都要提供一个构造函数,保证每个对象正确的初始化为有意义的值。特别是指针数据类型,应初始化为合法指针或0。
42:
不要让类的public成员函数返回对类的private数据成员的非const引用(或指针),返回这种引用会破坏类的封装。
43:
将类中所有友元关系的声明放在类的首部之后,不要在其前面加上任何成员访问说明符。

C语言中常见错误

C语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下“灵活的余地”,但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误。看着有错的程序,不知该如何改起,本人通过对C的学习,积累了一些C编程时常犯的错误,写给各位学员以供参考。
1.书写标识符时,忽略了大小写字母的区别。
main()
{
int a=5;
printf("%d",A);
}
编译程序把a和A认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。
2.忽略了变量的类型,进行了不合法的运算。
main()
{
float a,b;
printf("%d",a%b);
}
%是求余运算,得到a/b的整余数。整型变量a和b可以进行求余运算,而实型变量则不允许进行“求余”运算。
3.将字符常量与字符串常量混淆。char c;
c="a";
在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“\”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a'和‘\',而把它赋给一个字符变量是不行的。
4.忽略了“=”与“==”的区别。
在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中可以写
if (a=3) then …
但C语言中,“=”是赋值运算符,“==”是关系运算符。如:
if (a==3) a=b;
前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a。由于习惯问题,初学者往往会犯这样的错误。
5.忘记加分号。
分号是C语句中不可缺少的一部分,语句末尾必须有分号。
a=1
b=2
编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。
{ z=x+y;
t=z/100;
printf("%f",t);
}
对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)。
6.多加分号。
对于一个复合语句,如:
{ z=x+y;
t=z/100;
printf("%f",t);
};
复合语句的花括号后不应再加分号,否则将会画蛇添足。
又如:
if (a%3==0);
I++;
本是如果3整除a,则I加1。但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将执行I++语句,不论3是否整除a,I都将自动加1。
再如:
for (I=0;I&lt;5;I++);
{scanf("%d",[$x)]
printf("%d",x);}
本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循环体变为空语句,此时只能输入一个数并输出它。
7.输入变

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值