layout: post title: c陷阱与缺陷笔记 categories: review tags: [c]

[toc]

这个是我印象笔记中的记录,最近准备弃用印象笔记,把笔记逐渐搬到github/豆瓣上。这个文章发到豆瓣秒删,不清楚豆瓣规则。发到这里

书写的很薄。课后题答案和附录占了一大部分。很快就看完了。不过还算有收获。

这本书尤其是书后的采访特别值得一看,有点八卦的感觉。像BS啊lippman这些人还有这书的作者(这夫妻俩同事也是c++沉思录和accelerated c++的作者)都在同一个实验室工作过。还有写标准模板库那哥们他们也认识。真是牛逼啊。牛逼的人周围也都是牛逼的人。随便举例都是“写boost库的那哥们”

####仔细梳理一下目录,其实我也没记住什么

词法陷阱讲的就是符号。注意贪心法(编译器的小偷懒),注意符号差异。

语法陷阱主要就是弄明白函数指针声明,还有const int*啥的。主要函数指针复杂化那个比较繁琐,需要分析清楚(还好有cdecl网站)

链接主要讲的是编译涉及到的声明,返回值,定义,避免重复定义,static.,extern.还没有深入讲就没了。

库函数主要讲了缓存,errno, signal。没仔细看。

预处理器主要讲的宏展开,也没有深入讲细节,只是注意小缺陷,之于一些tricks没有讲。

可移植性缺陷是分析一些库函数实现,和int大小之类。

附录和答案讲了一些我比较感兴趣的(有趣的)玩意,手录下来。

#1 避免c语言中问题技巧是知道自己在做什么(记得linus也说过c++背后给你优化一些东西太过于邪恶,C语言清晰明了但是这取决于程序员)

最令人生厌的问题都来自哪些看起来能工作实际上隐藏着各种bug的程序

“思考”是一切错误之源;我可以轻易地举出事实来证明这一点:犯了错的人总是会说,“哦,可是我原以为…… ”只要大键琴的各种部件还没有粘合到一起,你就应该反复思考直到真正理解,这种“思考”是无妨的。你应该在你不用粘合剂的情况下把所有的部件拼装起来(称为演习或排练),研究它们是如何结合的,并与装配图仔细对照。

在你把某些部件粘合起来后,还应该再检查一遍,我听过很多次这种不幸的故事:“做完我做了什么什么,可是今天早上再看就…… ”

亲爱的制作者,如果你昨晚就好好看了的话,那么你可能已经把不合适的部件拆下来重装好了,很多制作者是利用业余时间动手DIY一个大键琴,所以经常忍不住要干到深夜,但是,根据我接听求助电话的经验,大多数错误都是在制作者上床睡觉之前的最后一件工作。所以,在你准备最后做一点什么之前,还是早点睡觉吧。

想想我们在工作中何尝不曾如此,在面临时间压力的情况下,对程序组合方式的理解尤为重要。编程者几乎都有过这样的经历:在调试程序很长时间之后,疲惫不堪的程序员开始漫无目的的瞎碰,这里试一下,那里改一点,如果凑巧程序似乎可以运行了,便万事大吉,这种工作方式往往最后导致一场灾难!

#2 测试程序,编译器是否支持嵌套注释

/* /* /0 */ * */ 1

#3 当一个程序异常终止时,程序输出的最后几行常常丢失,原因以及采取什么措施解决

原因是异常终止的程序可能没机会来清空其输出缓冲区,因此程序生成的输出可能崔在内存的某个位置,但却永远不会被写出了。在某些系统撒很难过,这些无法被写出的输出数据可能长达好几页。

对于试图调试这类程序的编程者来说,这种丢失输出的情况经常会误导他们,隐晦他啊会造成一种假象。实际上程序发生失败的时刻要比实际运行失败的真正时刻要早得多。解决办法就是在调试时强制不允许对输出进行缓冲,

setbuf(stdout,(char *)0 );

这句在输出stdout前执行。(main函数的第一个语句)

#4 可变域宽与精度

printf(%.%s\n, 12, 5, str)

printf(%*%\n, n)

#5 varargs.h的实现(没仔细看以后时间仔细看吧)

就这样囫囵吞枣的看完了。


采访全文

Koenig和Moo夫妇访谈

作者:Andrew R. Koenig, Barbara Moo

采访:王曦,孟岩译者:孟岩

【译者注】Andrew Koenig和Barbara Moo夫妇是C++领域内国际知名的技术专家、技术作家和教育家。最近,他们的几部著名作品《C++沉思录》(Ruminations on C++),《C陷阱与缺陷》(C Traps and Pitfalls)和Accelerated C++中文版即将问世。作为C++ View的成员和《C++沉思录》一书的技术审校,我与C++ View电子杂志的主编王曦一起对Koenig夫妇进行了一次email采访。下面是这次采访的中文译稿。

【Koenig的悄悄话】你们问的问题,我们已经答复如下。大部分问题,我们都是分别回答的,有些问题我们两个一起回答,个别情况,只有一个人作答。我们是在尼亚加拉瀑布度假期间完成这次采访的,我脑子里一直在想,对我们的中国读者说些什么好呢?这事想得我头疼。也许结束度假之后,我们能说得更好些。

提问:请向我们介绍你们自己的一些情况好吗?“Koenig”是个德国姓吗?怎么发音呢?“Moo”呢?

Koenig:“Koenig”是一个很常见的德国姓,在德文里写成“König”,意义是“国王(king)”。不过我的情况很特殊。我祖上是波兰和乌克兰人,不是德国人。这个名字其实是一个长长的波兰姓氏的缩写。我读自己名字的时候,重音放在前面的音节,整体的音韵类似“go”的发音。而一些与我同名的人发音时,第一个音节的音韵类似“way”的发音,我们家里人从来不这么说。**

Moo: 谈到我这个姓氏,最重要的一点就是,其发音跟牛叫的声音一摸一样——当我还是孩子的时候,小伙伴们经常模仿牛叫声来取笑我。我父辈从斯堪迪纳维亚移民来美国,这个姓是个挪威姓。我在自己的C++技术生涯中最快乐的时刻之一,就是在遇到Simula阵营里的Kristen Nygaard时,他告诉了我这个姓氏的起源。他说这个姓氏多少反映了我祖先居住的地方——Moo是一个很少见的挪威姓氏,其意义是“荒芜的平原”,既不是亚欧大陆上那种一望无际、水草丰茂的大草原,也不是沙漠。我想不是个很浪漫的姓氏,不过能够跟祖先联系起来,还是很有趣的。顺便一提,中国读者可能会对以下事实感兴趣。很多人在见到我之前,都以为我是中国人。我甚至收到过来自中国的电话推销,希望我去中国作一次远程旅行,认祖归宗。

提问:Stanley Lippman在《Inside the C++ Object Model》一书中提到了贝尔实验室的Foundation项目,他这么说:“这是一个很令人激动的项目,不仅仅因为我们所作的事情令人激动,而且我们的团队同样令人激动:Bjarne, Andy Koenig, Rob Murray, Martin Carroll, Judy Ward, Steve Buroff, Peter Juhl, 当然还有我自己。除了Bjarne和Andy之外所有的人都归Barbara Moo管理。她经常说,管理一个软件开发团队,就像放牧一群骄傲的猫。”请问,这段与Bjarne和其他人共事的日子,对你们二位真的那么美好吗?

Koenig:**那一段日子,在我看来,不过是我长达15年的C++生涯中的一部分,而Foundation项目里的人也只不过是一个更大社群中的一部分。当时我已经开始在标准委员会中开展工作,所以我不仅要与同一屋檐下的人讨论,还要经常与全世界各地的数十位C++程序员互相交流。

Moo: 我倒是更喜欢当年围绕cfront的那段工作经历,cfront是最早的C++编译器,那是一个伟大的团队,而且我们处于一个新语言的创造中心,一种新的、更好的工作方法的创造中心。那是一段令人激动的时光,我将永远保存在记忆里。

提问:作为C++标准委员会的项目编辑,哪件事情最令您激动?我们都知道,是您鼓励Alex Stepanov向标准委员会提交STL,并建议将其并入标准库。关于这个传奇故事,您还能向我们透露一些细节吗?

Koenig:那次Barbara和我跑到位于加州保罗阿尔托的斯坦福大学去教授一星期的C++课。当时Alex Stepanov在惠普实验室工作,也在保罗阿尔托,我们以前在AT&T共事过,所以对他之前的工作有所了解。很自然的,我们邀请他共进午餐。席间他非常兴奋的提起他和他的同事正在开发的一个C++库。不久之后,标准委员会在圣何塞开会,那里距离保罗阿尔托只有不到一小时车程。我觉得Alex的想法实在很有意思,就邀请他给标准委员会的成员讲了一课。我们都觉得,当时标准化的工作已经十分接近完成,他的工作不可能对标准构成什么影响。但是,我们至少应该让委员会成员知道它的存在,起码以后我们可以说STL是被拒了,而不是我们孤陋寡闻,致有遗珠之撼。那次交流会是我所参加过的技术报告中最令人激动的几个之一。在长长的一天之后,会议接近结束的时候,一半人已经疲惫不堪——可是Alex的精力极其充沛,而且他的思想如此先进,大大超越我们之前见过的任何东西。因此,当会议快结束时,委员们开始认真地讨论,是否应该讲这个库并入C++标准。当然,后来这个库就被渐渐纳入标准,但其实际过程还是相当惊险的。有好几次至关重要的投票,都可以把它扼杀掉。有一次,程序库子委员会甚至决定投票拒绝考虑Alex的建议,幸好我及时指出,我们通常的议事规程是,先解决旧的议题,然后再考虑新的议题,就算是准备拒绝建议,也不应该违例。我们围绕Alex的建议展开了大量的讨论,最后,终于有足够多的人改变了主意,促使委员会逐渐接受了它。

提问:您二位对于现在的C++教育状况怎么看?我们是否应该更加重视标准库教育,而不是语言细节的教育?或者您有别的看法?

Koenig: 当前C++的教育状况实在太糟糕了。很多所谓的C++教材不过是C语言书,只是在结尾粘贴一点点C++的材料而已。结果呢,他们告诉读者,字符串乃是定长字符数组,应该用标准库中的strcpy和strcmp来操作。一个程序员一旦在一开始掌握了这些东西,就会根深蒂固,多年挥之不去。就其本身而言,C++是一种非常低级的语言。唯有利用库,才能写出高层次的程序来。初学者还不能自己构造库,所以他们要么用现成的标准库,要么自己去写低层次的程序。确实有不少程序应该用低层次技术来构造,但是对于初学者不合适。

Moo: 当然是库优于语言细节。两个原因:首先,学生们可以不必费力包装低层次的语言细节,从而更容易建立整体语言的全局观念,了解到其真实威力。根据我们的经验,学生们首先掌握如何使用程序库之后,就会很容易理解类的概念,学会如何构造类的技术。如果首先去学习语言细节,那么就很难理解类的概念及其功能。这种理解上的缺陷,使他们很难设计和构造自己的类。不过,更重要的一点是,首先学习程序库,能够使学生培养起良好的习惯,就是复用库代码,而不是凡事自己动手。首先学习语言细节的学生,最后的编程风格往往是C类型的,而不是C++风格:他们不会充分地运用库,而自己的程序带有严重的C主义倾向——指针满天飞,整个程序都是低层次的。结果是,在很多情况下,你为C++的复杂性付出了高昂代价,却没有从中获得任何好处。

提问:在《C++沉思录》中,你们提到:“C++希望面对把实用性放在首位的社群”。不过在实践中,很多程序员都在抱怨,要形成一个好的C++设计实在是太难了,他们觉得Java甚至老式的C语言都比C++更为实用。这种看法有什么错误吗?你们对奉行实用主义的C++程序员有何建议?

Koenig: 你们中国人有没有类似这样的谚语:“糟糕的手艺人常常责怪自己的工具”?还有一句,“当你手里拿着锤子的时候,整个世界都成了钉子”。编程问题彼此不同。在我看来,就一个问题产生良好的设计方案的途径,就是使用一种允许你进行各种设计的工具。这样一来,你就可以选择最适合该问题的设计方案。如果你选择了这样的工具,那么你就必须负责选择合适的设计方案。

Moo: 关于这个问题,我想用一个项目的实例来说明,那时AT&T最早采用C++开发的项目之一。他们在写一个已经建成的系统的第二版,所以认为对问题域已经有足够深入的了解。他们估计学习C++是整个工作中比较困难的一部分。然而实际上,他们在开发中发现,他们对问题领域并没有很好的理解。于是花费了大量的时间来形成正确的抽象。设计是很困难的,语言问题相对容易得多。我们相信,C++在运行时性能上做了一个很好的折中,能够在“一切都是对象”的语言与“避免任何抽象”的语言之间取得恰到好处的平衡。这就是C++的实用性。

提问:有一点看起来你们与几乎所有的C++技术作家意见不同。其他人都高声宣扬,面向对象编程乃是C++最重要的一面。而你们认为模板才是最重要的。我仔细阅读了《C++沉思录》中有关OOP的章节,发现你们所给出的几个例子和解决方案在某些方面是很相似的。你们是否认为所有“良好”的面向对象解决方案都具有某种共同的特质?是否在很多情况下,OO都不如其他的风格?为什么认为“基于对象”和“基于模板”的抽象机制优先于面向对象抽象机制?

Koenig: 所谓面向对象编程,就是使用继承和动态绑定机制编程。如果你知道有一个很好的程序使用了继承和动态绑定,你能做出怎样的推断?在我们看来,这意味着该程序中有两个或两个以上的类型,至少有一个共同的操作,也至少有一个不同的操作。否则,就不需要继承机制。此外,程序中必然有一个场景,需要在运行时从这些类型中挑选出一个,否则就不需要动态绑定机制。再考虑到,我们所举的例子必须足够短小精悍,能够放在一本书里,还不能让读者烦心,所以对我们来说,很难在所有这些限制条件下想出很多不同的程序范例。某些面向对象编程语言,如Python,其所有类型都是动态的,那么技术书籍作者就不会面对这样的问题。例如,C++中的容器类大多数用模板写成,因其可以容纳毫无共同之处的对象,所以要求元素类型必须是某个共同基类的派生类毫无道理。然而,在Python中,容器类中本来就可以放置任何对象,所以类似模板那样的类型机制就不必要了。所以,我认为你所看到的问题,其实是因为很难找到又小又好的面向对象程序来做范例,才会产生的。而且,对于其他语言必须烦劳动态类型才能解决的问题,C++能够使用模板来高效地解决。

Moo: 我同意,我们写的东西让你很容易地得出上述结论。但是在这个特例里,我不认为我们所写的东西代表了我们的全部观点。我们针对C++写了很多的介绍性和提高性的材料。在这本书里,“基于对象设计”中的抽象机制就已经很难掌握了,而又必须在介绍面向对象方法之前讲清楚。所以,我们所写的东西实际上是想展示我们这样的观点:除非你首先掌握了构造良好类的技术,否则急急忙忙去研究继承就是揠苗助长。另一个因素是,我们希望用例子来推进我们的教学。若要展示良好的面向对象设计,问题可能会变得很复杂。这种例子没法很快掌握,也不适合那本书的风格。

提问:如果说我只能记住你的一句话,那一定是这句:“用类来表示概念”。你在《C++沉思录》这本书里,反复强调这句话,给我留下极其深刻的印象。假设我能再记住一句话,你们觉得应该是什么?

Koenig & Moo: 避免重复。如果你发现自己在程序的两个不同部分里做了相同的事情,试着把这两个部分合并到一个子过程中。如果你发现两个类的行为相近,试着把这两个类的相似部分统一到基类或模板中。

提问:你们在《C++沉思录》中有两句名言:“类设计就是语言设计,语言设计就是类设计。”你们对C++标准库的未来如何看待?人们是应该开发更多的实用组件,比如boost::thread和regex++,还是继续激进前行,支持不同的风格,像boost::lambda和boost::mpl所做的那样?

Koenig: 我觉得现在回答这个问题还为时尚早。从根本上讲,C++语言反映了其社群的状况,而当前整个社群里各种声音都有。我看还需要一段时间才能达成共识,确定发展的方向。

提问:有时,编写平台无关的C++程序比较困难,而且开发效率也不能满足需求。您是否认为把C++与其他的语言,尤其类似Python和TCL/TK那样的脚本语言合并使用是个好主意?

Koenig: 是的。我最近在学习Python,得出的看法是,Python和C++构成了完美的一对组合。Python程序比相应的C++程序短小精悍,而C++程序则比Python快得多。因此,我们可以用C++来构造那些对性能要求很高部分,然后用Python把它们粘在一起。Boost中的一个作者Dave Abrahams写了一个很不错的C++库,很好地处理了C++与Python的接口问题,我认为这是个好的想法。

提问:你们的著名作品《C 陷阱与缺陷》,《C++沉思录》和《Accelerated C++》中文版即将问世。想对你们的中国读者说些什么?

Koenig & Moo: 我们应该保持谦虚,有很多人已经从我们的书中学到了一些东西。我们很高兴将会有一个很大的群体成为我们读者群的一部分,希望你们从书中有所收获。

提问:我在你们的主页上看到不少漂亮的照片。你们有没有访问中国的计划?那一定可以让你们拍到更多的好照片。

Koenig几乎所有的照片都是用一架中型照相机拍摄的,它又大又重,以至于在1995年,有一次旅行时,我们被禁止把它带上飞机。当然,现在飞机对于行李的控制更加严格了,所以我觉得不太可能带着这台相机去中国旅游。现在我只在车程范围内进行严肃的艺术摄影。

提问:最后一个问题,我们都希望成为更好的C++程序员。请给我们三个你们认为最重要的建议,好吗?

Koenig & Moo:

  1. 避免使用指针;
  2. 提倡使用程序库;
  3. 使用类来表示概念