假设库A有a()和b().如果我将程序B与A链接并调用a(),b()是否包含在二进制文件中?编译器是否看到程序中的任何函数调用b()(可能a()调用b()或另一个lib调用b())?如果是这样,编译器如何获取此信息?如果没有,如果我链接到一个大型库但只使用次要功能,这不是对最终编译大小的大浪费吗?
看看链接时优化.这必然取决于供应商.它还取决于您如何构建二进制文件.MS编译器(至少2005年起)提供了一种称为功能级链接的东西- 这是剥离你不需要的符号的另一种方式.这篇文章解释了如何通过GCC实现同样的目标(这是旧的,GCC必须继续前进,但内容与您的问题相关).
另请参阅LLVM实现(以及示例部分).
我建议你也看看John Levine的Linkers and Loaders--一本很好的读物.
这取决于.
如果库是共享对象或DLL,则会在运行时加载库中的所有内容.额外内存的成本(希望)通过在使用该库的内存中的所有进程之间共享库(实际上是代码页)来抵消.对于像libc
.so这样的东西来说,这是一个巨大的胜利myreallyobscurelibrary.so
.但是你可能并不是在询问共享对象.
静态库只是单个目标文件的集合,每个目标文件都是单独编译(或汇编)的结果,甚至可能不用相同的源语言编写.每个目标文件都有许多导出的符号,并且几乎总是有许多导入的符号.
链接器的工作是创建一个没有剩余未定义导入符号的已完成可执行文件.(当然,我在撒谎,如果允许动态链接,但请耐心等待.)为此,它首先在链接命令行上显式命名的模块(可能隐含在其配置中),并假设任何模块明确命名必须是完成的可执行文件的一部分.然后,它尝试查找所有未定义符号的定义.
通常,命名的对象模块期望从某些库中获取符号,例如libc.a
.
在您的示例中,您有一个调用该函数的模块a()
,这将导致链接器查找导出的模块a()
.
你说该库命名为A(在UNIX上,可能libA.a
)报价a()
和b()
,但你不指定如何.你暗示a()
并且b()
不要互相打电话,我会假设.
如果libA.a
是从构建a.o
和b.o
其中每个限定相应的单一功能,则链接器将包括a.o
和忽略b.o
.
但是,如果libA.a
包括ab.o
定义了两者a()
,b()
那么它将包含ab.o
在链接中,满足需要a()
,并包括未使用的功能b()
.
正如其他人所提到的,有些连接器能够将各个功能从模块中分离出来,并且只包括实际使用的那些功能.在许多情况下,这是安全的事情.但是,除非您有特定的文档,否则通常最安全的做法是假设您的链接器不会这样做.
别的东西要注意的是,大多数的接头尽量少传球,因为他们可以通过被命名为命令行上的文件和库,并建立自己的符号表,因为他们去.实际上,这意味着总是在链接命令行上的所有对象模块之后指定库是一种好习惯.