C程序设计新思维 第2版(txt+pdf+epub+mobi电子书下载)


发布时间:2020-11-23 08:03:44

点击下载

作者:(美)Ben Klemens 克莱蒙

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

C程序设计新思维 第2版

C程序设计新思维 第2版试读:

前言

C就是Punk Rock

虽然C仅有为数不多的关键词,并且没有那么多细节修饰,但是[1]它很棒!你可以用C来做任何事情。它就像一把吉他上的C、G和D弦,你很快就可以掌握其基本原理,然后就得用你的余生来提高。不理解它的人害怕它的威力,并认为它粗糙得不够安全。虽然没有企业[2]和组织花钱去推广它,但是实际上它在所有的编程语言排名中一直被认为是最流行的语言。

这门语言已经有几十年的历史了,可以说已经进入了中年。创造它的是少数对抗管理阶层并遵从完美的punk rock精神的人;但那是20世纪70年代的事情了,现在这门语言已经历尽沧桑,并且成为主流的语言。

当punk rock变成主流的时候人们会怎样?在其从20世纪70年代出现后的几十年里,punk rock已经从边缘走向中心:The Clash、The Offspring、Green Day和The Strokes等乐队已经在全世界卖出了几百万张唱片(这还只是一小部分),我也在家附近的超市里听过被称为grunge的一些精简乐器版本的punk rock分支。Sleater-Kinney乐队的前主唱还经常在自己那个很受欢迎的喜剧节目中讽刺punk rocker[3]音乐人。对这种持续的进化,一种反应是划一条界限,将原来的风格称为punk rock,而将其余的东西称为面向大众的粗浅的punk。传统主义者还是可以播放20世纪70年代的唱片,但如果唱片的音轨磨损了,他们可以购买数码版本,就像他们为自己的小孩购买Ramones牌的连帽衫一样。

外行是不明白的。有些人听到punk这个词时脑海里就勾画出20世纪70年代特定的景象,经常的历史错觉就是那个时代的孩子们真的在做什么不同的事情。喜欢欣赏1973年Iggy Pop 的黑胶唱片的传统主义者一直是那么兴趣盎然,但是他们有意无意地加强了那种punk rock已经停滞不前的刻板印象。

回到C的世界里,这里既有挥舞着ANSI’89标准大旗的传统主义者,也有那些拥抱变化,甚至都没有意识到如果回到20世纪90年代,他们写的代码都不可能被成功编译与运行的人。外行人是不会知道个中缘由的。他们看到从20世纪80年代起至今还在印刷的书籍和20世纪90年代起至今还存于网上的教程,他们听到的都是坚持当年的软件编写方式的、死硬的传统主义者的言论,他们甚至都不知道语言本身和别的用户都在一直进化。非常可惜,他们错过了一些好东西。

这是一本打破传统并保持C语言punk精神的书。我对将本书的代[4]码与1978年Kernighan和Ritchie出版的书中的C标准进行对比毫无兴趣。既然连我的电话都有512MB内存,为什么还在我的书里花费章节讲述如何为可执行文件减少几千字节呢?我正在一个廉价的红色上网本上写这本书,而它却可以每秒运行3 200 000 000条指令,那为什么我还要操心8位和16位所带来的操作的差异呢?我们更应该关注如何做到快速编写代码并且让我们的合作者们更容易看懂。毕竟我们是在使用C语言,所以我们那些易读但是并没有被完美优化的代码运行起来还是会比很多烦琐的语言明显更快。[5]

问题:这本书与其他书有什么不同?

答案:有些书写得好,有些书写得有趣,但是大部分C语言的教科书都非常相像(我曾经读过很多这样的教科书,包括[Griffiths,2012]、[Kernighan,1978]、[Kernighan,1988]、[Kochan,2004]、[Oualline,1997]、[Perry,1994]、[Prata,2004]和[Ullman,2004]。多数教材都是在C99标准发布并简化了很多用法之后写成的,你可以看到现在出版的这些教材的第N版仅仅在一些标注上做了一点说明,而不是认真反思了如何使用这门语言。它们都提到你可以拥有一些库来编写你自己的代码,但是书籍完成时,缺少了保障库的可靠性和可移植性的安装与开发环境。那些教科书现在仍然有效并且具有自己的价值,但是现代的C代码已经看起来和那些教科书里面的不太一样了。

这本书与那些教科书的不同之处,在于对这门语言及其开发环境进行了拾遗补漏。书中讲解的方式是:直接使用提供了链表结构和XML解析器的现成的库,而不是把这些从头再写一次。这本书也体现了如何编写易读代码和用户友好的函数接口。

问题:这本书的目标读者是谁?我需要是一个编程大师吗?

答案:你必须有某种语言的编程经验,或许是Java,或者是类似于Perl的某种脚本语言。这样我就没有必要再向你讲为什么你不应该写一个很长的没有任何子函数的函数了。

本书的内容假设你已经有了通过写C代码而获得的C语言的基本知识。附录A提供了一个简短的有关C语言基础的教程,那些以前写Python和Ruby等脚本语言的读者可以阅读它。

请允许我介绍我写的另一本关于统计和科学计算的教科书Modeling with Data [Klemens,2008]。那本书不仅提供了很多关于如何处理数值和统计模型的内容,它还可以用作一本独立的C语言的教材,并且我认为那本书还避免了很多早期教材的缺点。

问题:我是个编写应用软件的程序员,不是一个研究操作系统内核的人。为什么我应该用C而不是像Python这类可以快速编程的脚本语言呢?

答案:如果你是一个应用软件程序员的话,这本书就是为你准备的。我知道人们经常认定C是一种系统语言,这让我觉得真是缺少了点punk的反叛精神——他们是谁啊?要他们告诉我们要用什么语言?

像“我们的语言几乎和C一样快,但更容易编写”这样的言论很多,简直成了陈词滥调。好吧,C显然是和C一样快,并且这本书的目的是告诉你C也像以前的教科书所暗示的那样容易使用。你没必要使用malloc,也没必要像20世纪90年代的系统程序员那样深深卷入内存管理,我们已经有处理字符串的手段,甚至核心语法也进化到了支持更易读的代码的境界。

我当初正式学习C语言是为了加速一个用脚本语言R编写的仿真程序。和众多的脚本语言一样,R具有C接口并且鼓励用户在宿主语[6]言太慢的时候使用。最终我的程序里有太多的从R语言到C语言的调用,最后我索性放弃了宿主语言。随后发生的事情你已经知道,就是我在写这本关于现代C语言技术的书。

问题:如果原本使用脚本语言的应用软件程序员能喜欢这本书当然好,但我是一名内核黑客。我在五年级的时候就自学了C语言,有时做梦都在正确编译。那这本书还有什么新鲜的吗?

答案:C语言在过去的几年里真的进化了很多。就像我下面要讨论的那样,各个编译器对新功能的支持的时间也不一样,感谢自从ANSI标准发布后,又发布了两个新的C语言标准。也许你应该读一下第10章,找找有什么能叫你感到惊讶的。本书的一部分,如讲解指针的经常被人错误理解的一些概念(第6章),也覆盖了自从1980年以后变化的内容。

并且,开发环境也升级了。很多我提到的工具,如make和debugger,你已经很熟悉了,但是我发现别人可能还不知道。Autotools已经改变了代码发布的方式,Git也改变了我们合作编程的方式 。

问题:我实在忍不住要问,为什么这本书中有差不多三分之一的篇幅都没有C代码?

答案:这本书本意就是讲述一些其他C语言教科书没有讲到的内容,排在首位的就是工具和环境。如果你没有使用调试器(独立的或者集成在你的IDE中),你就是在自讨苦吃。教科书经常把debugger放到最后面,有的根本就不提。与他人共享代码也需要另外的工具集,如Autotools和Git。代码并不存在于真空里,其他的教科书都在假设读者只需要了解C语言语法就会有生产力了,那就让我写一点不同于这些教科书的内容吧。

问题:有太多的用于C开发的工具,你在本书中如何取舍呢?

相比大部分语言,C语言社区有更高的内部互通性。GNU提供了太多的C语言扩展,还有那些只工作在Windows平台的IDE,只存在于LLVM中的编译器扩展等。这就是为什么过去的教科书不去讲解工具的原因。但是现在,有些系统应用得非常普遍。很多工具来自于GNU;LLVM和相关工具虽然不是主流,但是也打下了坚实的基础。不管你用什么,Windows、Linux或者你从你的云计算提供商那里取得的任何东西,这里我介绍的工具全部是容易并且可以快速地安装的。我提到了一些平台相关的工具,但是仅限那么几例。

我并没有介绍集成开发环境(IDEs),因为很少的集成开发环境能跨平台工作(尝试建立一个Amazon Elastic Computer Cloud实例,然后在上面安装Eclipse和它的C插件),而且IDEs的选择大部分被个人的喜好所左右。IDE有一个项目建造系统,它通常与别的IDE的项目建造系统不兼容。IDE的项目文件在你分发到外面的时候就不能用了。除非你硬性规定所有的人(在教室、特定办公室或者某些计算平台上)都必须使用相同的IDE。

问题:我能上网,一两秒的功夫就能找到命令和语法的细节。那么说真的,为什么我还要读这本书?

答案:的确。在Linux或Mac机器上你只要用一个带有 man operator 的命令行就能查到运算符优先级表,那么我为什么还要把它放在这本书里?

我可以和你上同样的Internet,我甚至花了很多的时间阅读网上的内容。所以我有了一个之前没谈到的、准备现在讲的好主意:当介绍一个新工具的时候,如gprof或者GDB,我给你那些你必须知道的方向,然后你可以去自己习惯的搜索引擎中查找相关问题。这也是其他教科书所没有的(这样的内容还不少呢)。标准:难以抉择

除非特别地说明,本书的内容遵从ISO C99和C11标准。为了使你明白这意味着什么,下面给你介绍一点C语言的历史背景,让我们回顾一下主要的C标准(而忽略一些小的改版和订正)。

K&R(1978前后)

Dennis Ritchie、Ken Thompson以及一些其他的贡献者发明了C语言,并编写了UNIX操作系统。Brian Kernighan和Dennis Ritchie最终在他们的书中写下了第一版关于这个语言的描述,同时这也是C语言的第一个事实上的标准[Kernighan,1978]。ANSI C89

后来Bell实验室向美国国家标准协会(ANSI)交出了这个语言的管理权。1989年,ANSI出版了他们的标准,并在K&R的基础上做出了一定的提高。K&R的书籍的第2版包含了这个语言的完整规格,也就是说在几万名程序员的桌子上都有这个标准的印刷版[Kernighan,1988]。1990年,ANSI标准被ISO基本接受,没有做重大的改变,但是人们似乎更喜欢用ANSI,89这个词来称呼这个标准(或者用来做很棒的T恤衫标语)。

10年过去了。C成为了主流,考虑到几乎所有的PC、每台Internet服务器的基础代码或多或少都是用C编写的,C语言已经成为了主流,这已经是人类的努力可以达到的最大的极限了。

在此期间,C++分离出来并大获成功(虽然也不是那么大)。C++是C身上发生的最好的事情了。当所有其他的语言都在试图添加一些额外的语法以跟随面向对象的潮流,或者跟随其作者脑袋里的什么新花招的时候,C就是恪守标准。需要稳定和可移植性的人使用C,需要越来越多的人把大量的金钱投入到了C++语言上,这样的结果就是:你好我好,大家过年,每个人都高兴。ISO C99

10年之后,C标准经历了一次主要的改版。为数值和科学计算增添了一些附加功能,如复数的标准数据类型以及泛型(type-generic)函数。一些从C++中产生的便利措施被采纳,包括单行注释(实际上起源于C的前期语言,BCPL),以及可以在for循环的开头声明变量。因为一些新添加的关于如何声明和初始化的语法,以及一些表示法上的便利,使用泛型函数变得更加容易。出于安全考量以及并不是所有人都说英语原因,一些特性也被做了调整。

当你想着单单C89的影响其实并不大,以及全球大范围地运行着C代码时,你就理解了ISO做出的任何改变都是会被广泛批评的——[7]甚至你不做任何改变,别人还想找茬骂你呢。的确,这个标准是有争论的。有两种常用的方式来表达一个复数(直角坐标和极坐标)——那么ISO会采用哪一个?既然所有的好代码都没采用变长的宏输入机制来编写,为什么我们还需要这个机制?换句话说,纯洁主义者批评ISO是屈服于外界的压力才给C语言增加了更多的特性。

当我写这本书的时候,多数的编译器在支持C99的同时都增加或减少了一些特性;如long double 类型看起来就引发了很多问题。然而,这里还是有一个明显的特例:Microsoft至今拒绝在其Visual Studio C++编译器中添加C99支持。在本书第6页“1.2 在Windows下编译C”一节中讲述了几种在Windows环境中编译C的方法,所以不能使用Visual Studio最多也就是有点不方便,这好比一个行业奠基人告诉我们不能使用ISO标准的C,这样标准就更有punk rock风格了。C11

觉察到了对所谓背叛行业趋势的批评后,ISO组织在第三版的标准中做出了为数不多的几个重大改变。我们有了可以编写泛型函数的方法,并且对安全性和非英语支持做出了进一步的改进。

C11标准在2011年12月发布后不久,编译器的开发者以惊人的速度完成了对新标准的支持。目前一些主流编译器已经声称做到了几乎全部的标准兼容。但是标准定义了编译器的行为,也定义了标准库和库支持。如线程和原子性等,有些系统上实现了,有些系统上还在开发。POSIX标准

事物的规律就是这样,伴随着C语言的进化,这门语言同时也和UNIX操作系统协同发展,并且你将会从本书中看到,这种相互协同的发展对日常工作是有意义的。如果某件事情在UNIX命令行中很容易利用,那么很有可能是因为这件事情在C中也很容易实现;某些UNIX工具之所以存在,也是为了帮助C代码的编写。UNIX

C和UNIX都是在20世纪70年代由Bell实验室设计的。在20世纪的多数时间里,Bell一直面临垄断调查,并且Bell有一项与美国联邦政府达成的协议,就是Bell将不会把自身的研究扩张到软件领域。所以UNIX被免费发放给学者们去研究和重建。UNIX这个名字是一个商标,原本由Bell实验室持有,但随后就像一张棒球卡一样在数家公司之间转卖。

随着其代码被不断研究、重新实现,并被黑客们以不同的方式改进,UNIX的变体迅速增加。因此带来了一点不兼容的问题,即程序或脚本变得不可移植,于是标准化工作的迫切性很快就变得显而易见。POSIX

这个标准最早由电气和电子工程师协会(IEEE)在1988年建立,提供了一个类UNIX操作系统的公共基础。它定义的规格中包括shell脚本如何工作,像ls、grep之类的命令行应该如何工作,以及C程序员希望能用到的一些C库等。举个例子,命令行用户用来串行运行命令的管道机制被详细地定义了,这意味着C语言的popen(打开管道)函数是POSIX标准,而不是ISO C标准。POSIX标准已经被改版很多次了;本书编写的时候是POSIX:2008标准,这也是当我谈到POSIX标准的时候所指代的。POSIX标准的操作系统必须通过提供C99命令来提供C编译器。

这本书用到POSIX标准的时候,我会告诉大家。

除了来自Microsoft的一系列操作系统产品,当前几乎所有你可以列举出的操作系统都是建立在POSIX兼容的基础上:Linux、Mac OS X、iOS、WebOS、Solaris、BSD——甚至Windows Servers也提供POSIX子系统。对于那些例外的操作系统,1.2“在Windows下编译C程序”一节将告诉你如何安装POSIX子系统。

最后,有两个POSIX的实现版本因为有较高的流行度和影响力,值得我们注意。BSD

在Bell实验室发布UNIX给学者们剖析之后,加州大学伯克利分校的一群好人做了很多明显的改进,最终重写了整个UNIX基础代码,产生了伯克利软件发行版(Berkeley Software Distribution,BSD)。如果你正在使用一台Apple公司生产的电脑,你实际上在使用一个带有迷人图形界面的BSD。BSD在几个方面超越了POSIX,因此我们还会看到,有一两个函数虽然不属于POSIX,但是如此有用而不容忽略(其中最重要的救命级函数是asprintf)。GNU

GNU这个缩写代表GNU’s Not UNIX,代表了另一个独立实现和改进UNIX环境的成功故事。大多数的Linux发行版使用GNU工具。有趣的是,你可以在你的POSIX机器上使用GNU编译器组合(GNU Compiler Collection,gcc)——甚至BSD也用它。并且,gcc对C和POSIX的几个方面做了一点扩充并成为事实上的标准,当本书中需要使用这些扩充的时候我会加以说明。

从法律意义上说,BSD授权比GNU授权稍微宽容。由于很多群体对这些授权的政治和商业意义深感担心,实际上你会经常发现多数工具同时提供GNU和BSD版本。例如,GNU 的编译器组合(gcc)和BSD的clang都可以说是顶级的C编译器。来自两个阵营的贡献者紧密跟随对方的工作,所以我们可以认为目前存在的差异将会随着时间逐渐消失。法律解读 美国法律不再提供版权注册系统:除了很少的特例,只要某人写下什么就自然获得了该内容的版权。发行某个库必然要通过将其从一个硬盘复制到另一个硬盘这样的操作,而且即便带有一点争议,现实中还是存在几种常用机制允许你有权利复制一个有版权的内容。● GNU公共许可证:其允许无限制地复制和使用源代码和可执行文件。不过有一个前提:如果你发行一个基于GPL许可证的源代码程序或库,你也必须将你的程序的源代码伴随程序发行。注意,如果你是在非商业环境下使用这样的程序,你不需要发行源代码。像用gcc编译你的源代码之类的运行GPL许可证的程序本身并不会使你具有发行源代码的义务,因为这个程序的数据(比如你编译出的可执行文件)并不认为是基于或派生于gcc的。例如:GNU科学计算库。● 次级GPL许可证:与GPL有很多相似,但是具有一个明显的区别:如果你以共享库的方式连接一个LGPL库,你的代码不算作派生的代码,也没有发行源代码的义务。也就是说,你可以采用不暴露源代码的方式发行一个与LGPL库连接的程序。例如:Glib。● BSD许可证:要求使用者维持BSD授权原始码原有的版权声明和免责声明,但不要求同时提供你的原始码。

请注意以下的免责声明:笔者不是律师,这段小常识只是完整法律文件的简单声明,读者如果无法判断自身所处的状况或者相关细节,请阅读原始文件或者请教律师。附加内容第2版

我以前是一个愤世嫉俗主义者,认为如果你写了第2版,那么你的主要目的就是让那些卖你第1版二手书的人不开心。本书的第2版如果没有第1版被发表的话,是不会,也不可能这么快的就出版的。(反正现在很多读者都在阅读电子版本了。)

与第1版相比,最大的增加就是并发线程,也就是并行计算部分了。它集中描述了OpenMP和原子变量和结构。OpenMP并不是C语言标准,但它是C生态系统中非常可靠的一部分,所以它应该在本书的范围内。原子变量是在2011年12月发布的C标准修订版中加入的,一年以后本书第1版出版的时候,还没有编译器去支持它。现在好了,我们不仅可以在理论上进行讲解,同时还可以有现实的实现和测试代码了。参考第12章。

第1版本得到了很多细心读者的反馈。他们发现了很多可能导致bug的内容,从一些我在命令行上使用的斜杠,到句子中的一些可能会引起误会的词。这个世界上没有什么东西是没有错的,但是有了读者的反馈,这本书现在更加的正确和有用了。

本版中添加的其他内容:● 附录A对于从其他语言转过来的读者,提供了一个C语言的简单

的教程。我本来不想在第1版中包含这一部分内容,因为有太多

的C语言教程了,但是包括了这部分内容让本书更加有用了。● 应大家的要求,我扩展了关于使用调试器的内容。见本书第32

页“2.1使用调试器”。● 第 1 版有一部分讲述了如何写一个接受变长参数的函数,如

sum(1,2.2)和sum(1,2.2,3,8,16)都是合法的。但是如果你想传送多

个变长列表的时候该怎么办呢?例如点积函数把两个变长向量相

乘,dot((2,4),(−1,1)和dot((2,4,8,16), (−1,1,−1,1)) 10.4“多列表”

介绍这一部分内容。● 我重写了第11章,利用新函数来扩展对象。主要添加的部分是

虚函数表的实现。● 关于预处理器,我也多写了一些,主要是在8.1.2“测试宏”中

介绍了一些测试宏的概念和用法。也包括了_Static_assert关键字。● 我一直坚守诺言,本书中不包括关于正则表达式解析的内容(因

为网上和其他书籍有太多这一部分内容了)。但是我在

13.2.1“解析正则表达式”部分加上了一个演示,展示了如何使

用POSIX的正则表达式解析函数。比起其他的语言,这些函数还

处在比较原始的形态上。● 第1版中关于字符串的讨论主要依赖于asprintf函数,它是一个

sprintf类似的函数,当写一个字符串的时候,会自动分配需要的

内存。这是一个被GNU广泛分发的版本,但是很多读者却被限

制使用,所以在本版本中,我加入了例子9-3,演示了如何利用

C语言标准的部分去实现这样一个函数。● 第7章最大的主题是精细地去管理那些会造成麻烦的数值类型,

所以第1版并没有提到很多在C99标准中新加入的数值类型。如

int_least32_t,uint_fast64_t等[C99,§7.18;C11,§7.20]。很多

读者鼓励我至少提一些有用的类型,如intptr_t和intmax_t,好吧,

我从善如流。本书使用的排版约定

本书使用如下排版约定:

斜体(italic)

用来表示新术语、URL、E-mail地址、文件名、文件扩展名等。

等宽字体(Constant width)

用来表示程序列表,同时在段落中引用的程序元素(例如变量、函数名、数据库、数据类型、环境变量、声明和关键字等)也用该格式表示。

等宽斜体(Constant width italic)

用于表示应该以用户提供的值或根据上下文决定的值加以替换的文本。 这个图标代表诀窍、建议和一般性的说明。

  这个图标表示警告或错误。使用示例代码

这是一本试图帮助你解决实际问题的书。总的来说,你可以在你的程序和文档中用本书的代码。除非你复制了太多的部分,你并不需要得到我的许可。例如,你的程序里使用了几段本书的代码并不需要得到许可。但是销售或发行含有O’Relly出版书籍中的源代码的确需要许可。通过引用本书及其源代码的方式来回答一个问题不需要得到许可。在你的文档中合并大量来自本书的代码不需要得到许可。

本书中用到的示范代码可以在以下地址找到:https://github.com/b-k/21st-Century-Examples。

我们感谢,但并不要求,您在引用时注明出处。一个引用通常包括书名、作者、出版商以及ISBN书号。例如,“C程序设计新思维(第2版)Ben Klemens(O’Reilly)。版权2014 Ben Klemens, 978-1-491-90389-6”。

如果你感觉你对示范代码的使用可能超出了上面列举的合理情况,你可以随时通过以下邮件地址联系我:permissions@oreilly.com。如何联系我们

如有对本书的评论或问题,请联系出版商:

美国

O’Reilly Media, Inc。1005 Gravenstein Highway North。Sebastopol, CA 95472。

中国

北京市西城区西直门南大街2号成铭大厦C座807室(100035)。奥莱利技术咨询(北京)有限公司。致谢

Nora Albert:慷慨的支持,豚鼠。

Jerome Benoit:Autoconf技巧。

Bruce Field、Dave Kitabjian、Sarah Weissman:严谨和彻底的审阅。

Patricj Hall:Unicode知识。

Nathan Jepson和Shawn Wallace:社论。

Andreas Klein:指出intptr_t的价值。

Rolando Rodriguez:测试、试用和调查。

Rachel Steely:出品。

Ulrik Sverdrup:指出我们可以使用重复指定初始化来设定默认值。

[1] “it rocks”,此处原文为双关语,借用英语中rock的不同含义,即“摇滚乐”和“很棒”。标题中的“punk rock”为流行于20世纪70年代的一种摇滚乐风格,以狂野反叛为特色,国内也称为“朋克”。——译者注

[2] 这篇前言明显地,而且是必须向Punk Rock Language: A Polemic致敬,作者是Chris Adamson。

[3] 像“can’t get to heaven with a three-chord song”这样的歌词,可能会让Sleater-Kinney被归类在后punk时期?不幸的是,没有ISO punk标准为各种音乐提供精确的定义。

[4] 从下文可以看到,该书的出版被认为是C语言诞生的标志性事件,业内常称为K&R。——译者注

[5] 这里作者将问题与解答比喻为C语言函数入口的参考引用。——译者注

[6] 这里指调用C语言的语言。——译者注

[7] 译者注:世界上就两种语言,没人用的和大家骂的。第1部分开发环境

在脚本语言花园围墙外的旷野里,有大量解决C语言的那些烦恼的工具,当然你必须自己去寻找。我之所以这么说,因为其中一些工具对于你轻松编写代码是完全必要的。如果你不使用调试器(无论是独立存在的还是集成于IDE环境的),你简直就是自找苦吃。

有很多已经存在的库,你可以在你的代码中去使用。这样你可以集中精力去处理手头的问题,而不是重新实现什么链表、解析器和其他一些基础性的东西。当使用外部库的时候,要保证你的程序的编译也尽量简单。

本书第1部分的内容简述如下。

第1章讲述如何设定基本开发环境,包括找到一个包管理器并利用这个工具安装所用的工具。这些背景知识足够我们体会有趣的内容,比如用从别处得到的库来编译程序。整个过程非常标准化,包括一小部分环境变量的设定和配置。

第2章介绍调试、文档管理和测试工具,因为直到调试、编档和测试都完成的时候,你的代码才能显示出良好的一面。

第3章讨论Autotools,这是一个用于打包并发布你的程序的工具。这一章选择了一种比较详尽的介绍方法,因此还包含了编写shell脚本和makefile的方法。

我们可不能像某些人那样把生活搞得太复杂。第4章介绍Git,一个用来追踪你和同事们的硬盘文件版本的微小改变的工具,以便使你尽可能简单地融合不同的版本。

其他的语言也是现在C语言开发环境的重要因素,因为太多的语言提供了C接口。第5章提供了一些如何编写这些接口的建议,并给你一个基于Python的扩展例子。第1章准备方便的编译环境

对于完成一些实际的项目,C语言的标准库是不够的。

事实上,C语言的生态系统已经延伸到了C语言标准之外。也就是说,如果你不想局限于只完成作业本上的习题,那么你必须知道如何方便地调用那些非ISO标准库以外的函数。如果你想处理XML文件、JPEG文件或者TIFF文件,那么你需要那些不是标准但是可以免费使用的libxml、libjpeg以及 libtiff库。不幸的是很多教科书都把这部分内容忽略了,这就使得读者不得不自己进行探索。这就是为什么很多批评C语言的人会说出不公平的言论:C语言是一门40岁的老语言了,所以你必须从头开始写很多有用的函数。这些读者根本就不知道如何使用外部的库。

以下是本章的主题。

设定必要的工具集

比起需要自行准备各种组件的黑暗时期,如今已经轻松很多了。你只需要10~15分钟就可以建立起完整的开发环境(当然得加上下载所需要的时间)。

编译一个C程序

你已经知道怎么做了,但是我们还需要设定一下需要链接哪些函数库,以及那些函数库所在的位置;只是输入cc myfile.c已经不够了。Make几乎是最简单的编译工具程序,我们就以它作为切入点开始介绍,先介绍一个最简单的makefile,虽然它很简单,但是有非常大的空间可以继续改进和扩充。

设定一些变量并加入一些新的库

无论我们使用什么系统,它们都会利用一小部分环境变量对自身进行定制。所以我们首先介绍这些环境是什么以及如何设定它们。一旦我们完成了基本的设定工作,只要稍微调整这些环境变量,就可以使用新加入的函数库了。

建立一个编译系统

作为回报,我们可以利用以上介绍的这些知识,建立一个简单的编译系统。在这个系统中,我们可以在命令行编译那些复制来的代码了。

对于使用IDE(集成开发环境)的用户来说,有一点值得特别说明:即使你不使用make,但是本部分内容依然和你有关。make使用的很多步骤,在IDE中都有对应的步骤。如果你知道make的内部机理,你就有能力对IDE做出更适合你的一些调整和设定。1.1 使用包管理工具

如果你没有使用过包管理工具,那你太落伍了。

这里介绍包管理工具有以下几个原因:首先,有些读者可能还没有安装过程序。对于这部分读者,我把这部分内容放到本书的开始,就是为了让你尽快地获得这些工具。一个好的包管理工具会让你很快地建立起一个POSIX子系统,其中包含很多你听说过的语言的编译器、很多的游戏、一些常见的办公应用软件以及几百个C语言的库等。

其次,对于C语言的开发者,包管理工具是日后我们安装C语言函数库的一个重要工具。

最后,当你从一个包的使用者成长为一个包的开发者的时候,本书会教你如何让自己的包更易于安装,这样当包存储库的管理员决定把你的代码包含进存储库的时候,管理员可以无障碍地建立最后的包。

如果你是一个Linux用户,并已经通过包管理器配置了电脑,你就已经知道了软件获取的过程非常简单。对于Windows系统的用户来说,我会在下面介绍Cygwin。对于使用Mac的用户有一些选项,例如Fink和Homebrew或者Macports,所有的这些选项都依赖于Apple的Xcode包。你可以在系统的安装光盘上找到它,或者到苹果的APP商店获取,或者通过注册成为苹果的开发者而获取(依赖于你的Mac电脑的具体生产年份)。

你需要什么包呢?这是C开发过程中最基本的问题。因为每一个系统都有不同的组织方式,所以包可能被包装在不同的地方,或者在基本包中默认安装,或者被命名为其他古怪的名字。如果你也不是很确定,那就先安装它再说,毕竟我们的电脑不会因为安装过多软件就变得不稳定或者运行慢了。但是你可能没有那么大的带宽来下载,或者没有那么大的硬盘来安装所有的包,所以你还需要做一些选择。如果你发现自己没有安装某些包,还可以回过头来重新安装。以下这些包是必须安装的。● 编译器,必须安装gcc;也可以选择安装Clang。● GDB调试器。● Valgrind,用于测试C内存使用错误。● Gprof,用于运行效率评测。● Make,使得你不用直接调用编译器。● Pkg-config,用于查找库。● Doxygen,用于生成程序文档。● Text editor, 目前有几百个文本编辑器可以选择,这里给出几个主

观的推荐。

——Emacs 和Vim是计算机老手的最爱。Emacs几乎涵盖了很多

的功能(E代表extensible,可扩展的);Vim更小型化,并且有

很多友好的功能键。如果你是一个编辑器的重度用户,那么你需

要从这两个编辑器中选择一个来进一步学习。

——Kate非常友好,也有吸引人的界面,能提供我们开发程序

时需要的一些功能,例如语法高亮。

——最后的一个选项是Nano,使用上最简单,是基于文本模式

的编辑器。即使你没有GUI界面,也可以使用它。● 如果你是IDE的爱好者,那就安装一个(或者几个)。同样有很

多的选择。这里只是给出一些推荐。

——Anjuta:属于GNOME家族,可以方便地使用 GNOME GUI

builder工具Glade。

——KDevelop:属于KDE家族。

——XCode:苹果公司OS X的IDE。

——Code::blocks:相对简单,在Windows下工作。

——Eclipse:拥有很多按钮和杯架的“豪华跑车”,跨平台。

在本章的后面,我还会介绍一些强力工具。● Autotools:Autoconf、Automake、libtool。● Git。● 其他的shell,例如Z shell。

当然,还有一些C库能够避免你重新发明“轮子”(更准确的类比是,重新发明“火车头”)。你可以获得更多的库,下面这些库是我们要在本书中用到的。● libcURL。● libGLib。● libGSL。● libSQLite3。● libXML2。

C语言库没有一致的命名标准,你必须要了解你的包管理器是如何将一个单独的包分解成不同的子部分的。一般有一个供用户使用的包,同时还有一个供开发者使用的包,所以请在选择基本的包的同时,选择带有-dev或者-devel的包。某些系统将文档分拆在独立的包中。也有的要求你单独下载带有调试符号表的包。如果你在没有调试符号表的包上运行GDB,那么GDB会要求你下载带有调试符号表的包。

如果你使用POSIX系统,在安装完所有前面提到的工具后,你就有了完整的开发环境,现在你可以开发你的程序了。对于Windows的用户,下面介绍如何在Windows操作系统下设置开发环境以便和Windows主系统进行沟通。1.2 在Windows下编译C程序

在大多数的系统下,C语言是主要的开发语言,其他的工具都以C语言为参照进行开发。但是Windows操作系统很奇怪地忽略了C语言。

因此我们需要一点时间来说明如何在Windows环境下设置开发环境。如果你不使用Windows系统,可以忽略这部分内容,直接跳到1.3“链接函数库的方式”一节。1.2.1 Windows中的POSIX环境

由于C语言起源于UNIX,并与UNIX一起演变,我们很难将两者分开来讨论。我想从POSIX开始介绍应该比较简单。如果想在Windows平台下编译其他平台下开发的程序,这是最自然的方法。

就作者所知,所有的操作系统可以分为两大类。● POSIX兼容系统。● Windows操作系统家族。

POSIX兼容并不代表系统的外观和UNIX相像,例如大部分Mac使用者完全不知道自己使用的是一个带有华丽界面的BSD系统。但是了解这部分知识的人却可以从应用程序→工具文件夹中启动终端(Terminal)应用程序,然后在其中运行ls、grep或者make等各种工具。

另外,并不是所有的系统都100%符合标准(例如Fortran 77编译器)。就本书来说,我们需要有一个基本的类似POSIX shell的shell、一些工具(sed、grep、make等)、一个C99编译器,以及fork和iconv等标准C语言库以外的函数库。这些工具和库可以作为主系统的补充。包管理工具相关的底层脚本、Autotools以及所有开发可移植代码的工具都在某种程度上依赖上面提到的那些工具和库。所以即使你不愿意与命令行打交道,你依然需要安装这些工具和库。

在作为服务器的操作系统以及完整的 Windows 7系统中,微软公司提供以前称为INTERIX,现在称为Subsystem for UNIX-based Applications(SUA)的子系统,包含常用的POSIX系统调用、Korn shell以及gcc。这个子系统默认不安装,需要你另行下载。目前版本的Windows不再提供SUA,Windows 8也不提供了,因此无法依赖微软自己提供的POSIX子系统了。

所以我们需要使用Cygwin。

如果想要从头建立Cygwin,可以参考下面的介绍。

1.为Windows撰写C函数库,提供所有的POSIX函数。这需要调整Windows和POSIX系统间的差异,例如Windows系统使用C:代表硬盘,而POSIX使用统一文件系统(Unified Filesystem)。对这种情况,可以为C:建立cygdrive/c,为D:建立cygdrive/d等别名。

2.现在可以编译POSIX标准程序,链接到上一步介绍的函数库,产生新的Windows版本的ls、bash、grep、make、gcc、x、rxvt、libglib、perl、python等。

3.建立好很多的程序和函数库以后,接着建立包管理工具,让使用者可以自己选择安装软件。

但是作为Cygwin的使用者,你不需要完成上面介绍的那些麻烦的步骤,只需要从Cygwin网站(http://cygwin.com)下载包管理工具,选择要安装的包,当然包括上面清单列出来的那些程序,再加上一个合适的终端(Terminal)(可以试试Mintyy,或者安装X系统中的Xterm,这两者都比Windows 自带的cmd.exe友好)。安装完后,你就可以看到开发系统所需要的各种豪华工具都在其中了。

在“路径”部分,我讨论了影响编译的各种环境变量,包括搜索文件的包含路径。这一部分并不是POSIX所独有的,Windows也有环境变量,你可以在控制面板的环境设置部分找到它们。如果你把Cygwin的bin路径(C:\cygwin\bin)加到Windows的PATH变量中,那么使用Cygwin的时候会更加方便。

现在你可以编译C代码了。1.2.2 在POSIX环境中编译C语言

微软公司在Visual Studio中提供了C++编译器,提供了与C89相容的模式(通常称为ANSI C,虽然C11是ANSI的当前标准)。这是目前微软公司提供的编译C语言的唯一方式,微软公司的很多代表都表示不会去支持C99标准(更别提C11标准了),Visual Studio是唯一一个还使用C89标准的编译器,因此我们需要其他替代方案。

当然,Cygwin提供了gcc,如果按照前面介绍的步骤安装好了Cygwin,那么你就有了完整的开发环境。

在Cygwin下编译的程序会依赖于Cygwin1.dll库中所提供的POSIX函数(不论你的程序是否使用任何POSIX相关的调用)。在有安装Cygwin的机器上执行这些程序不会有任何问题,使用者可以通过双击执行这些程序,系统能够找到cygwin1.dll。如果要在没有安装cygwin1.dll的机器上运行这个程序,你必须与程序同时提供cygwin1.dll文件。在作者的机器上,路径是/bin/Cygwin1.dll,Cygwin1.dll采用类似GPL的授权方式(参考前言部分的“法律解读”),如果将dll从Cygwin分离出来而单独与你的应用程序一起分[1]发,那你必须提供程序的源代码。

如果有困难,你需要用不依赖于Cygwin1.dll的方式来重新进行编译。也就是要你的程序中使用MinGW,而不去使用POSIX相关的函数(像fork和popen函数),就像我们后面介绍的那样。利用cygcheck可以发现你的应用程序依赖于那些DLL,从而验证你的执行程序是否连接到了Cygwin1.dll。 查看一个程序或者动态链接库依赖于哪些库,用下面的命令:● Cygwin: cygcheck libxx.dll。● Linux: ldd libxx.so。● Mac: otool -L libxx.dylib。1.2.3 在非POSIX环境中编译C语言

如果你的程序不需要调用POSIX函数,你可以使用MinGW(Minimalist GUN for Windows),它提供了一个标准的C编译器和一些基础工具。MSYS是与MinGW伴生的,提供了另外的一些工具和shell。

MSYS提供了一个POSIX shell(你可以使用Mintty或者RXVT终端来运行你的shell)。或者干脆不使用任何命令行,只是使用Code::blocks,这是一个在Windows上使用MinGW的IDE集成环境。Eclipse是一个功能更加丰富的可以配置成与MinGW一起工作的IDE集成环境,虽然这需要更多一点的配置工作。

不过如果POSIX命令行能让你感觉更舒服些,那就安装Cygwin,下载提供MinGW版本的gcc,用这些编译环境而不是用与POSIX连接的、默认版本的Cygwin gcc。

如果你还没有遇到过Autotools,那你会很快遇到。通过Autotools建立软件包的三个标志性命令是:./configure、make和make install。MSYS提供了足够的机制以保证可能让包在MinGW环境下安装;否则你就必须从Cygwin的命令行来安装这个包,但是你可以用下面的命令来配置这个包,使用Cygwin的MinGW32编译器来产生与POSIX无关的代码:/.configure –host=ming32

然后像通常那样运行make和make install。

在MinGW下编译后,不管是通过命令行还是Autotools,你都会得到一个Windows本地的二进制代码。因为MinGW并不知道cygwin1.dll的存在,你的程序并不会有任何POSIX调用。你得到的将是一个真正的Windows程序,没有人会知道你是从POSIX环境编译出来的。[2]

然而,MinGW的真正问题是缺乏预编译库。如果你想摆脱cygwin1.dll,那么你也不能使用与Cygwin一起发行的libglib.dll版本。你将必须从源代码重新将GLib编译成一个Windows 动态链接库——但是Glib在国际化方面依赖GNU的gettext,所以你需要先把那个库编译一遍。现代代码依赖于现代的库,所以你可能会发现自己花费了很多时间来处理类似的工作,而在别的系统上可能只是一行包管理器的命令。如果这样,我们就真的如某些人所说,C已经40岁了,你需要从头写每样东西。

所以,下面就是几句忠告。微软公司拒绝在C语言标准的支持上与别人沟通,任由其他人实现后grunge时代的C编译器。Cygwin完成了这些工作并提供了全功能的包管理器,带有可以满足你的多数或全部工作的足够多的库,但这些都伴随着POSIX风格的代码和Cygwin的DLL。如果你觉得这是一个问题,那么你将不得不多做很多工作,以重建那些优雅的代码所需要的整个环境和库。1.3 链接函数库的方式

有了编译器,有了POSIX的工具包,还有一个可以用来方便地安装几百个库的包管理器。现在我们开始用这些工具来编译程序。

我们必须从编译器命令行开始,这会很快给我们带来很多麻烦,

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载