2.2.3 使用库文件
到此,了解了一些基本的术语,现在可以学习如何来使用库了。
使用库必须:
1)包含库的头文件。
2)使用库中的函数和变量。
3)把库连接进可执行程序。
目标模块没有加入库时,也可执行上述步骤。对于C/C++的分段编译,包含头文件和连接目标模块是基本步骤。
2.2.3.1 连接器如何查找库
当C或C++要对函数和变量进行外部引用时,根据引用情况,连接器会选择两种处理方法中的一种。如果还未遇到过这个函数或变量的定义,连接器会把它的标识符加到“未解析的引用”列表中。如果连接器遇到过函数或变量定义,那么这就是已解决的引用。
如果连接器在目标模块列表中不能找到函数或变量的定义,它将去查找库。库有某种索引方式,连接器不必到库里查找所有目标模块—而只需浏览索引。当连接器在库中找到定义后,就将整个目标模块而不仅仅是函数定义连接到可执行程序。注意,仅仅是库中包含所需定义的目标模块加入连接,而不是整个库参加连接(否则程序会变得毫无意义的庞大)。如果想尽量减小程序的长度,当构造自己的库时,可以考虑一个源代码文件只放一个函数。这要求更多的编辑工作[1],但它对使用者来说是有益的。
因为连接器按指定的顺序查找文件,所以,用户使用与库函数同名的函数,把带有这种函数的文件插到库文件名列表之前,就能用他自己的函数取代库函数。由于在找到库文件之前,连接器已先用用户所给定的函数来解释引用,因此被使用的是用户的函数而不是库函数。注意,这可能是一个bug,并且C++名字空间禁止这样做。
2.2.3.2 秘密的附加模块
当创建一个C/C++可执行程序时,连接器会秘密连接某些模块。其中之一是启动模块,它包含了对程序的初始化例程。初始化例程是开始执行C/C++程序时必须首先执行一段程序。初始化例程建立堆栈,并初始化程序中的某些变量。
连接器总是从标准库中查找程序中调用的经过编译的“标准”函数。由于标准库总可以被找到,所以只要在程序中包含所需的头文件,就可以使用库中的任何模块,并且不必告诉连接器去找标准库。例如,标准的C++库中有iostream函数。要用这些函数,只需包含<iostream>头文件即可。
如果使用附加的库,必须把该库文件名添加到由连接器处理的列表文件中。
2.2.3.3 使用简单的C语言库
用C++来编写代码,并不禁止用C的库函数。事实上,整个C的库以默认方式包含在标准的C++库中。这些函数代替用户做了大量的工作,因此,使用它们,可以节约许多时间。
本书将尽可能地使用标准的C++库函数(也包含标准C库函数),但是只有用标准库函数才能保证程序的可移植性。在某些情况下必须使用非标准C++库函数的地方,我们也将尽量使用符合POSIX标准的函数。POSIX是基于UNIX上的一个标准,它包括的函数是C++库中没有的。通常能在UNIX(特别是Linux)平台上找到POSIX函数,也可能在DOS/Windows下找到。例如,如果要用到多线程编程,最好使用POSIX线程库,这样的代码就容易理解、端口通信和维护(POSIX线程库通常只用到操作系统提供的基本的线程设施)。
[1]我推荐使用Perl或Python自动完成这项任务作为程序员库打包过程的一部分(参见www.Perl.org或www.Python.org)。