
下载

下载
第2章 数 据 抽 象
C + +是一个能提高效率的工具。为什么我们还要努力(这是努力,不管我们试图做的转变
多么容易)使我们从已经熟悉且效率高的语言(在这里是 C语言)转到另一种新的语言上?而
且使用这种新语言,我们会在确实掌握它之前的一段时间内降低效率。这归因于我们确信通过
使用新工具将会得到更大的好处。
用程序设计术语,多产意味着用较少的人在较少的时间内完成更复杂和更重要的程序。然
而,选择语言时确实还有其他问题,例如运行效率(该语言的性质引起代码臃肿吗?)、安全性
(该语言能有助于我们的程序做我们计划的事情并具有很强的纠错能力吗?)、可维护性(该语
言能帮助我们创建易理解、易修改和易扩展的代码吗?)。这些都是本书要考察的重要因素。
简单地讲,提高生产效率,意味着本应花费三个人一星期的程序,现在只需要花费一个人
一两天的时间。这会涉及到经济学的多层次问题。生产效率提高了,我们很高兴,因为我们正
在建造的东西功能将会更强;我们的客户(或老板)很高兴,因为产品生产又快,用人又少;
我们的顾客很高兴,因为他们得到的产品更便宜。而极大提高效率的唯一办法是使用其他人的
代码,即使用库。
库,简单地说就是一些人已经写的代码,按某种方式包装在一起。通常,最小的包是带有
扩展名如L I B 的文件和向编译器声明库中有什么的一个或多个头文件。连接器知道如何在 L I B
文件中搜索和提取相应的已编译的代码。但是,这只是提供库的一种方法。在跨越多种体系结
构的平台上,例如U N I X ,通常,提供库的最明智的方法是用源代码,这样在新的目标机上它
能被重新编译。而在微软Wi n d o w s 上,动态连接库是最明智的方法,这使得我们能够利用新发
布的D D L经常修改我们的程序,我们的库函数销售商可能已经将新 D D L发送给我们了。
所以,库大概是改进效率的最重要的方法。 C + +的主要设计目标之一是使库容易使用。这
意味着,在C中使用库有困难。懂得这一点就对 C + +设计有了初步的了解,从而对如何使用它
有了更深入的认识。
2.1 声明与定义
首先,必须知道“声明”和“定义”之间的区别,因为这两个术语在全书中会被确切地使
用。“声明”向计算机介绍名字,它说,“这个名字是什么意思”。而“定义”为这个名字分配
存储空间。无论涉及到变量时还是函数时含义都一样。无论在哪种情况下,编译器都在“定义”
处分配存储空间。对于变量,编译器确定这个变量占多少存储单元,并在内存中产生存放它们
的空间。对于函数,编译器产生代码,并为之分配存储空间。函数的存储空间中有一个由使用
不带参数表或带地址操作符的函数名产生的指针。
定义也可以是声明。如果该编译器还没有看到过名字 A,程序员定义int A,则编译器马上
为这个名字分配存储地址。
声明常常使用于e x t e r n关键字。如果我们只是声明变量而不是定义它,则要求使用 e x t e r n。
对于函数声明,e x t e r n是可选的,不带函数体的函数名连同参数表或返回值,自动地作为一个
声明。

函数原型包括关于参数类型和返回值的全部信息。 int f(float,char);是一个函数原型,因为
它不仅介绍f这个函数的名字,而且告诉编译器这个函数有什么样的参数和返回值,使得编译
器能对参数和返回值做适当的处理。 C + + 要求必须写出函数原型,因为它增加了一个重要的安
全层。下面是一些声明的例子。
在函数声明时,参数名可给出也可不给出。而在定义时,它们是必需的。这在 C语言中确
实如此,但在C + +中并不一定。
全书中,我们会注意到,每个文件的第一行是一个注释,它以注释符开始,后面跟冒号。
这是我用的技术,可以利用诸如“ g r e p”和“a w k ”这样的文本处理工具从代码文件中提取信
息。在第一行中还包含有文件名,因此能在文本和其他文件中查阅这个文件,本书的代码磁盘
中也很容易定义这个文件。
2.2 一个袖珍C库
一个小型库通常以一组函数开始,但是,已经用过别的 C 库的程序员知道,这里通常有更
多的东西,有比行为、动作和函数更多的东西。还有一些特性(颜色、重量、纹理、亮度),
它们都由数据表示。在C语言中,当我们处理一组特性时,可以方便地把它们放在一起,形成
一个s t r u c t。特别是,如果我们想表示我们的问题空间中的多个类似的事情,则可以对每件事
情创建这个s t r u c t的一个变量。
这样,在大多数C库中都有一组 s t r u c t和一组活动在这些 s t r u c t 上的函数。现在看一个这样
的例子。假设有一个程序设计工具,当创建时它的表现像一个数组,但它的长度能在运行时建
立。我称它为s t a s h 。
第2章 数 据 抽 象 23
下载

在结构内部需要引用这个结构时可以使用这个 s t r u c t 的别名,例如,创建一个链表,需要
指向下一个s t r u c t 的指针。在C库中,几乎可以在整个库的每个结构上看到如上所示的 t y p e d e f。
这样做使得我们能把s t r u c t作为一个新类型处理,并且可以定义这个s t r u c t 的变量,例如:
stash A, B, C;
注意,这些函数声明用标准 C 风格的函数原型,标准 C 风格比“老”C 风格更安全和更
清楚。我们不仅介绍了函数名,而且还告诉编译器参数表和返回值的形式。
s t o r a g e指针是一个unsigned char*。这是 C 编译器支持的最小的存储片,尽管在某些机器
上它可能与最大的一般大,这依赖于具体实现。人们可能认为,因为 s t a s h被设计用于存放任何
类型的变量,所以 v o i d * 在这里应当更合适。然而,我们的目的并不是把它当作某个未知类型
的块处理,而是作为连续的字节块。
这个执行文件的源代码(如果我们买了一个商品化的库,我们可能得到的只是编译好的
O B J或L I B 或D D L等)如下:
24 C + + 编程思想
下载

注意本地的 #include 风格,尽管这个头文件在本地目录下,但仍然以相对于本书的根目录
给出。这样做,可以创建不同于这本书根目录的另外的目录,很容易拷贝文件到这个新目录下
去实验,而不必担心改变 #include 中的路径。
第2章 数 据 抽 象 25
下载