何时使用动态库 vs. 静态库
当使用动态库还是静态库的问题是因为以下原因出现的:
动态库(dll)的优点是可以在多个应用程序之间重用/共享代码;在调用该代码时才加载到进程内存中,不需要时可以卸载;可以独立于程序的其余部分进行升级。
动态库的缺点是加载dll和代码重定位会对性能产生影响;版本控制问题("dll hell")。
静态库(lib)的优点是代码始终加载在进程中,没有性能影响,也没有代码重定位;没有版本控制问题。
静态库的缺点是占用可执行文件/进程的空间,所有代码都在可执行文件中,在进程启动时加载;不能重用/共享代码,每个产品都有自己的代码副本。
代码重定位也可以在构建时使用rebase.exe或通过将/BASE选项传递给link.exe来完成。这取决于运行时是否存在任何意外的地址空间冲突,才能确定是否有效。
使用动态库还是静态库取决于具体的需求和考虑因素。
动态库和静态库的使用时机
在使用静态库时需要注意以下几个问题:
1. 单例模式:如果某个对象需要是全局唯一的,并且将其放在静态库中,需要小心。如果多个动态链接库与该静态库链接,每个动态链接库都会得到一个自己的单例对象的副本。然而,如果你的应用程序是一个没有自定义动态链接库的单个可执行文件,这可能不是一个问题。
2. 未使用代码的删除:当你链接静态库时,只有被你的动态链接库/可执行文件引用的部分会被链接到你的动态链接库/可执行文件中。例如,如果mylib.lib包含a.obj和b.obj,而你的动态链接库/可执行文件只引用了a.obj中的函数或变量,那么b.obj的整个内容将被链接器丢弃。如果b.obj包含全局/静态对象,它们的构造函数和析构函数将不会被执行。如果这些构造函数/析构函数有副作用,你可能会对它们的消失感到失望。
同样地,如果静态库包含特殊的入口点,你可能需要确保它们实际上被包含在内。在嵌入式编程中的一个例子(好吧,不是Windows),是一个被标记为特定地址的中断处理程序。你还需要将中断处理程序标记为入口点,以确保它不会被丢弃。
这样做的另一个后果是,静态库可能包含由于未解决的引用而完全无法使用的目标文件,但直到你引用了这些目标文件中的函数或变量才会引发链接器错误。这可能发生在编写库之后很长时间。
3. 调试符号:你可能希望为每个静态库使用单独的PDB文件,或者希望将调试符号放置在目标文件中,以便将其合并到DLL/EXE的PDB文件中。Visual C++文档中解释了必要的选项。
4. RTTI:如果将单个静态库链接到多个动态链接库中,可能会导致多个相同类的type_info对象。如果你的程序假设type_info是“单例”数据,并使用&typeid()或type_info::before(),可能会得到意想不到的结果。
至于关于单例的问题,不要忘记DLL可能会被多次加载(相同版本或多个版本),并且仍然不能保证单例。
关于未使用代码删除的补充说明:对DLL进行的调用也需要一个实际的调用来强制加载被引用的DLL。将其添加为引用,但然后不包括任何引用它的调用将得到与不调用任何内容的静态库相同的结果。唯一的区别是实际上发布的内容。在这两种情况下,静态构造函数和析构函数都不会被触发。
不应该发生这种情况。.a文件将始终包含其构建时的所有符号。当它静态链接到应用程序中时,是的,只有使用的符号将被链接。
我还不理解你的大部分内容,但对于我们这些Windows用户来说,为我们提供了一些具体的信息,给你加一点赞。
当使用动态库和静态库时,不同的情况会导致不同的选择。静态库会增加二进制文件的大小,它们总是被加载,无论你使用的是哪个版本的代码,运行的都是你编译的那个版本。而动态库是单独存储和版本化的,如果更新与原始版本兼容,可能会加载动态库的版本与最初与代码一起提供的版本不同。此外,动态库不一定会被加载,通常是在首次调用时加载,并且可以在使用相同库的组件之间共享(多个数据加载,一个代码加载)。
在Windows/Mac上(没有软件包管理器),使用动态库而不是静态库没有什么好的理由。因为Windows DLL不可重定位,代码共享通常不起作用(通常每个应用程序都会运行和使用自己的库版本)。唯一的好处是更容易更新库。
“可重定位”的具体含义是什么?
在Mac上,我使用了很多动态库。例如,Mac OS X内置了sqlite3。我创建了一个具有sqlite3数据库功能的程序,用于高效存储。然而,由于它很少被使用,动态链接可以节省编译时间,使测试更加容易/快捷。然而,如果我要构建发布版本,我认为我会始终使用静态库,以防兼容性问题。
在Windows和Linux上都有共享库的“加载时重定位”的概念。使位置无关代码成为可能的最重要的因素并不是Linux的特别之处,而是x64指令集添加的RIP相对寻址;Windows和Linux都可以利用RIP相对寻址来减少重定位库时的修正次数。
静态库的互操作性较差,在.NET环境中使用起来更加困难。
值得一提的是,有时由于许可限制,如果希望保持代码库闭源,你需要将应用程序与库动态链接,比如使用LGPL的Qt。
一旦代码在进程中完全加载,静态库和动态库之间会有性能差异吗?