书名:C/C++代码调试的艺术(第2版)
ISBN:978-7-115-60806-2
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 张海洋
责任编辑 傅道坤
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
读者服务:
微信扫码关注【异步社区】微信公众号,回复“e60806”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。
本书围绕C/C++程序调试这一主题,系统深入地介绍了在Windows和Linux操作系统上如何高效地调试C/C++程序。
本书分为11章,内容涵盖了程序调试的基本知识、Visual C++调试的基本功能与技巧、Linux系统中gdb工具的使用、死锁调试、动态库调试、内存检查、远程调试、转储文件调试分析、发行版调试,以及调试的高级话题和调试方面的扩展知识。
本书作为学习C/C++调试技术的重要资料,讲解通俗易懂,选取的示例注重理论与实际的联系。无论是C/C++的初学者,还是经验丰富的开发人员,都会从中受益。
张海洋,云坞科技联合创始人,清华大学计算机专业毕业,从事软件开发近20年,曾在外企工作10余年,长期工作在开发第一线,已经申请软件发明专利10余项。精通C/C++、Python等编程语言,在Windows驱动、Linux驱动、Windows/Linux系统开发和调试方面具有丰富的经验。
——“有人的地方就有江湖,有软件的地方就有BUG。”
比尔·盖茨在1998年演示Windows 98操作系统的时候,突然出现蓝屏,但是他一点也不惊慌,因为他知道这就是真实的软件世界。
盖茨表现淡定还有另外一个原因,那就是在Windows蓝屏以后,会生成崩溃转储文件,软件工程师可以根据该文件来分析蓝屏的原因,能够快速地定位并解决BUG。
在C/C++领域工作的这10多年里,令我印象深刻的并不是使用C/C++去实现一个复杂的功能有多么困难,而是解决一个看似微不足道的BUG并不像我们想象的那么容易。很多读者可能也有这个体会,长时间地熬夜、加班,并不是为了完成一项重大的任务或者实现一个新功能,而通常是为了解决一个不容易发现的BUG——这个BUG可能是别人留下的,也可能是自己留下的。C/C++开发人员通常有很强的代码编写能力,可以完成复杂的任务。常言道“代码写得越多,BUG就会越多”,这是事实。如何才能又快又好地开发出高质量的软件呢?这也是软件开发人员一直在思考的问题,所以很多组织和培训机构都从软件开发的外围入手,比如使用一定的开发模式和方法,增加或者改变软件开发的流程等。的确,这些措施能够在一定程度上提升软件开发的效率。
但是BUG并没有减少,因此作者希望能够将这些年积累下来的解决C/C++程序中的BUG的经验整理成书,希望能够帮助读者在开发工作的初期避免一些本不应该出现的BUG。即使是在开发工作的后期出现BUG,相信读者也能够有效地使用本书介绍的调试手段和技巧,迅速地定位并解决BUG。
本书坚持理论结合实际,融入了作者10多年的Windows系统和Linux系统开发经验,尤其是C/C++开发方面的调试经验与心得。除第1章外,其他每章都编写了示例代码,无论是在Windows还是Linux系统中开发,本书都竭尽所能把问题解释清楚,确保每一位读者都能从本书中获得宝贵的调试技巧与方法。
通过本书的示例代码,读者可以熟练掌握书中介绍的调试工具、调试方法和调试技巧。本书虽然无法做到面面俱到,但是只要读者掌握了相关理论以及相应的实战技巧,就一定能够提升调试技术,在解决BUG时产生事半功倍的效果。
全书共分为11章,几乎涉及C/C++程序调试的方方面面,其中包括在Windows系统和Linux系统中调试C/C++程序的方法与技巧,下面简单介绍一下每章的内容。
● “第1章C/C++调试基本知识”主要介绍了什么是BUG,还介绍了与调试有关的一些概念,以及C/C++调试的重要性。
● “第2章Visual C++调试基本功能”详细介绍了Visual C++的基本调试功能,包括断点管理、调试执行、监视/快速监视、内存查看、即时窗口、调用堆栈、多线程管理等,还介绍了一些断点的高级用法,比如条件断点、函数断点、数据断点等。
● “第3章Linux系统下gdb调试基本功能”详细地介绍了Linux系统下gdb的调试技巧与方法,包括gdb的断点管理、查看变量、查看内存、查看调用栈、线程管理,还介绍了一些gdb特有的调试功能,比如观察点、捕获点等。
● “第4章多线程死锁调试”介绍了一些多线程的基本知识,以及多线程同步与死锁的概念,然后通过Windows系统和Linux系统中的死锁调试实例来演示如何解决死锁问题,最后介绍了如何在多线程环境中避免死锁。
● “第5章调试动态库”介绍了Windows系统和Linux系统动态库的一些基本知识,并简单演示了如何在Windows系统和Linux系统中开发动态库,最后详细介绍了在Windows系统和Linux系统中调试动态库的多种方法。
● “第6章内存检查”介绍了如何调试、分析和发现C/C++代码中的内存错误,比如内存泄漏、堆栈溢出等,并详细地介绍了如何在Windows系统和Linux系统中发现和解决内存泄漏和堆栈溢出问题。
● “第7章远程调试”介绍了远程调试的多种方法与技巧,既包含Windows系统的远程调试方法,也包含Linux系统的远程调试方法,同时还介绍了在Windows系统中如何远程调试Linux程序。
● “第8章转储文件调试分析”主要介绍了如何生成转储(dump)文件,以及如何在Windows系统和Linux系统中分析死锁转储文件和崩溃转储文件。
● “第9章发行(Release)版调试”介绍了发行版与调试版的一些区别,解释了为什么发行版不容易进行调试,并演示了如何在Windows系统和Linux系统中调试发行版。
● “第10章调试高级话题”介绍了一些与调试有关的高级话题,比如断点的秘密、与Windows调试和Linux调试相关的API和系统调用、使用gdb“破解”软件密码等。
● “第11章调试扩展知识”介绍了在Windows系统和Linux系统中使用C/C++开发驱动的一些入门知识,并通过一个示例演示了如何创建第一个驱动程序,包括如何调试驱动,以及如何分析内核转储文件等,最后介绍了用Visual Studio 2022调试C/C++程序的新特性。
本书所有的示例代码可以从异步社区下载,也可以使用Git客户端工具从地址https://github.com/SimpleSoft-2020/book_debug.git下载。如果使用的是Windows系统,那么可以使用VC 2019打开debug_examples.sln解决方案文件。如果使用的是Linux系统,就可以进入每个目录,然后直接执行make命令来编译和运行示例代码。
本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。
您还可以扫码右侧二维码, 关注【异步社区】微信公众号,回复“e60806”直接获取,同时可以获得异步社区15天VIP会员卡,近千本电子书免费畅读。
本书提供源代码。要获得这一配套资源,请在异步社区本书页面中单击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,单击“发表勘误”,输入勘误信息,单击“提交勘误”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券或样书。
扫描下方二维码,您将会在异步社区微信服务号中看到本书信息及相关的服务提示。
我们的联系邮箱是fudaokun@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线投稿。
如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也让我们有动力持续为您提供有价值的内容。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社30余年的IT优质出版资源和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。
异步社区
微信服务号
什么是BUG呢?BUG的本意是虫子,现在泛指计算机硬件或者软件中的错误、缺陷等。我们随处可以听到BUG这个词:电梯运行不稳定,就会有人说“电梯出BUG了”;手机App功能不正常,也会有人说“App出BUG”了。这种表达方式没错,但是虫子为什么会成为软硬件错误的代名词呢?关于BUG一词的来源,还有一个小小的传说。
这个传说与计算机科学家Grace Hopper有关。
Grace Hopper是著名的计算机科学家,而且是第一位获得美国海军少将军衔的女性。1947年9月9日,作为程序员的Grace Hopper发现Mark II计算机出现了故障。经过一番排查之后,她发现引起计算机故障的原因是一只死了的飞蛾卡在了计算机的某个电器元件中。当把这只飞蛾取出来后,故障也就解决了。Grace Hopper当时把这件事记录了下来,并且把那只飞蛾粘贴在当天的工作手册中,并写下了“First actual case of bug being found.”的字句,如图1-1所示。
图1-1 BUG来源的传说
从此这个故事就广为流传,后来BUG一词就用来指代计算机硬件或者软件中的错误以及缺陷等。
那么什么是Debug呢?我们都知道,在英文中De是前缀,具有“去除”和“离开”的意思,比如detach(分离),因此Debug就是去除BUG的意思。但是怎样去除BUG呢?过程就是Debug,我们一般不将Debug称作除错,而是叫作调试,因为反复的调试过程才能去除BUG。调试过程很复杂,要修改代码、借助工具进行测试等,有时候甚至比开发一个软件还要复杂。
很多人可能都会有这种感觉,即我们开发一个小功能可能只需要1个小时,但是去除其中的错误可能会花费一天甚至更长的时间。事实表明,我们在发现问题、解决问题的过程中往往需要更多的智慧与技巧。
加拿大著名的计算机科学家Brian Wilson Kernighan说过一句有趣的名言:“调试代码的难度是编写代码的两倍,如果编写代码的时候已经黔驴技穷,那你便没有足够的聪明才智去调试它了。”原文如图1-2所示。
图1-2 Brian Wilson Kernighan有关调试的言论
这句话看似简单,实则对调试充满敬畏。的确,虽然编写和调试代码看起来密不可分,但是确实都需要特别的能力。无论是编写代码还是调试代码,都需要发挥我们的聪明才智。
调试的形式多种多样,只要是为软件去除BUG的过程或者行为,甚至有时候不一定是去除BUG的过程或者行为(比如优化),都可以被称为软件调试。在我们的印象中,好像只有在调试器中运行软件才叫作调试。其实调试有很多种方式,比如我们可以在调试器中运行软件进行调试,也可以分析软件的转储文件去进行调试,等等。我们可以将软件调试定义为“发现和去除BUG的过程或者行为”。
要解决BUG,首先要定位BUG的根源(root cause),然后为BUG提出解决方案。定位BUG根源的过程往往要比提出解决方案困难很多,一旦找到了问题根源,就会有各种方案来解决问题。
有一个小故事很好地诠释了定位BUG和为BUG提出解决方案这两者之间的关系。
20世纪初,美国福特公司正处于高速发展时期,多个车间和厂房被迅速建成并投入使用,客户的订单堆满了福特公司销售处的办公室。福特汽车供不应求。就在这时,福特公司的一台电机出了问题,这几乎导致整个车间不能运转,相关的生产工作也被迫停了下来。公司调来大批检修工人反复检修,又请来许多专家进行检查,却始终没有找到问题的根源,更谈不上维修了。这时有人提议去请著名的电机专家Charles Proteus Steinmetz(如图1-3所示)来帮忙。
图1-3 Charles Proteus Steinmetz
Steinmetz 仔细检查了电机,然后用粉笔在电机外壳画了一条线,对工作人员说:“打开电机,将记号处里面的线圈少绕16圈。”令人惊异的是,工作人员照办后,故障竟然排除了,福特公司很快就恢复了生产。
福特公司经理问Steinmetz要多少酬金,Steinmetz说:“不多,只需要1万美元。”“1万美元?就只简简单单画了一条线!”Steinmetz看大家迷惑不解,转身开了个清单:画一条线,1美元;知道在哪儿画线,9999美元。福特公司经理看了之后,不仅照价付酬,还重金聘用了Steinmetz。
这个故事用在这里非常合适,不仅体现了发现问题的根源比提出解决方案更重要,而且说明了找到问题的根源是一种技能,也更有价值。我们调试的目的通常是找到“画线”的地方,这就是调试的重要性。
C/C++语言已经发展了40多年,生命力也越来越旺盛,在TIOBE世界编程语言排行榜中,C语言长期保持前三位。2020年6月的TIOBE世界编程语言排行榜如图1-4所示。
图1-4 2020年6月的TIOBE世界编程语言排行榜
C/C++语言受到广泛欢迎的原因是C/C++语言几乎无所不能。Windows系统和Linux系统中的绝大部分代码是用C/C++语言实现的,只有一小部分代码是汇编代码。由于C/C++比其他语言具有更高的性能,因此对性能要求比较高的系统大多会选择C/C++语言进行开发。
与硬件相关的一些应用开发场景也是非C/C++语言莫属,比如单片机开发、无人机系统开发、物联网应用开发等,都需要使用C/C++语言。无论是Windows系统还是Linux系统中的内核驱动模块开发,一般都只能使用C/C++语言来实现。
尽管C/C++语言的学习难度可能比其他语言更大,学习周期更长,但是真正掌握了C/C++语言后,再学习其他语言就会容易得多。很多编程语言本身也是用C/C++语言开发的,例如Python语言。
本书主要介绍如何调试C/C++代码,如果掌握了本书的调试方法与技巧,其他语言的调试也能够驾轻就熟。
本书中关于调试方面的方法与技巧长期有效,而且很多方法与技巧也适用于早期的软件产品,比如Visual C++ 6.x(发布于1998年)。无论操作系统以及调试软件怎样升级换代,这些基本的调试方法与技巧都是通用的。如果掌握了Visual C++软件的调试方法与技巧,就可以将代码轻松地迁移到Dev-C++中进行调试,甚至能够将Java代码平滑地迁移到Eclipse中调试——尽管界面有所不同,但是软件调试的核心是相同的。
调试器(Debugger)是用来调试软件的工具,是开发人员的得力助手。有效的工具能够使我们的工作得心应手,BUG也会无所遁形。调试器有很多种类,例如Windows系统的Visual Studio和Linux系统的gdb等。无论使用哪种调试器,我们只有熟练地掌握它们,才能使其发挥巨大作用。
工欲善其事,必先利其器。要想快速地发现BUG,解决BUG,必须要掌握使用这些调试器的基本方法和技巧。
本书主要用到的调试器是Visual Studio的Visual C++和Linux系统的gdb,后面的章节会进行详细的介绍。
读者服务:
微信扫码关注【异步社区】微信公众号,回复“e60806”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。