本文主要是介绍显式链接和隐式链接,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
显式链接(Explicit Linking)和隐式链接(Implicit Linking)是两种动态链接库(dll/so)的使用方式,它们在程序与动态库之间的交互方式上存在显著差异。
显式链接(Explicit Linking)
定义与特点:
显式链接是指在程序运行时,由程序代码主动加载动态链接库到进程的地址空间中。这种方式允许程序在需要时才加载特定的库,从而增加了程序的灵活性和可控性。
显式链接通常通过调用系统提供的API函数(如Windows下的LoadLibrary或Linux下的dlopen)来实现,这些函数会加载指定的dll/so文件,并返回一个句柄或指针,用于后续的函数调用。通过GetProcAddress(Windows)或dlsym(Linux)等函数,程序可以获取dll/so中导出函数的地址,并通过函数指针进行调用。
应用场景:
- 插件系统:允许程序在运行时加载不同的插件,实现功能的扩展和定制。
- 可选功能:某些功能可能不是必需的,可以在用户需要时通过显式链接加载相应的dll/so。
- 跨平台兼容:通过条件编译和显式链接,可以实现跨平台的代码复用和部署。
隐式链接(Implicit Linking)
定义与特点:
隐式链接是最为常见的,所有的编译环境默认都是采用隐式链接的方式使用动态库。隐式链接会在链接生成可执行程序时就确立依赖关系,在该程序启动时,操作系统自动会检查它依赖的动态库, 并一一加载到该程序的内存空间,程序员就不需要操心什么时候加载动态库了。
隐式链接的实现通常涉及到包含动态库的头文件(.h)和链接动态库。在编译时,编译器会使用这些文件来解析对动态库中函数的引用,并在链接阶段将相关信息嵌入到可执行文件中【1】。
【1】链接动态库,是在编译阶段链接器将库的头文件和库等信息嵌入到可执行程序中。如:
# -lcgicc: 指示链接器在构建可执行文件或库时,要链接到名为 libcgicc 的动态库或静态库。
# 这里的 lib 前缀和 .so(对于动态库)或 .a(对于静态库)后缀在命令行中通常是省略的
arm-linux-gnueabihf-g++ -o myapp myapp.cpp -lcgicc
当程序运行时,操作系统会根据可执行文件中的信息自动查找并加载所需的动态库文件。如果找不到动态库文件或者库文件中的函数与可执行文件中的引用不匹配,程序将无法正常运行。
应用场景:
- 大多数桌面应用程序:这些应用程序通常依赖多个动态库来提供丰富的功能和用户界面。隐式链接可以简化开发过程并降低维护成本。
- 标准库和框架:如C++标准库、.NET框架等系统级或应用级框架通常通过隐式链接的方式提供给开发者使用。
显式链接优缺点
优点:
- 灵活性高:显式链接允许程序在运行时根据需要动态地加载和卸载dll/so(动态链接库),这提供了高度的灵活性。程序可以根据用户的操作、系统状态或其他条件来决定是否加载某个dll/so,从而优化资源使用。
- 错误处理能力强:在显式链接中,如果dll/so文件不存在、损坏或版本不兼容,程序可以捕获这些错误并作出相应的处理,如显示错误消息、尝试加载其他版本的dll/so或回退到不使用该dll/so的功能。
- 减少启动时间:由于dll/so是在程序运行时根据需要加载的,因此可以减少程序启动时的加载时间。这对于包含大量可选功能的大型应用程序尤其有用。
- 支持插件机制:显式链接是实现插件机制的基础。通过动态加载插件dll/so,程序可以在不修改自身代码的情况下扩展新功能。
缺点:
- 编程复杂度增加:显式链接需要程序员在代码中显式地处理dll/so的加载、函数地址的获取和错误处理等问题,这增加了编程的复杂度。
- 性能开销:每次加载和卸载dll/so时,操作系统都需要执行一系列操作来管理内存和文件资源,这可能会引入一定的性能开销。
- 依赖管理复杂:显式链接的程序需要显式地指定dll/so的路径和名称,这增加了依赖管理的复杂性。如果dll/so的路径或名称发生变化,程序可能无法正确加载dll/so。
隐式链接优缺点
优点:
- 使用简单:隐式链接通过链接器将dll/so的导入信息嵌入到可执行文件中,并在程序启动时由操作系统自动加载所需的dll/so。这使得程序员无需在代码中显式地处理dll/so的加载和函数地址的获取等问题。
- 依赖管理相对简单:由于dll/so的导入信息被嵌入到可执行文件中,因此操作系统可以根据这些信息自动查找和加载dll/so,这简化了依赖管理。
- 性能开销较小:由于dll/so是在程序启动时由操作系统自动加载的,因此与显式链接相比,隐式链接在加载dll/so时的性能开销可能较小。
缺点:
- 灵活性较低:隐式链接的程序在启动时会自动加载所有依赖的dll/so,这限制了程序的灵活性。如果某个dll/so不是必需的,但在程序启动时被加载了,那么就会浪费内存和其他系统资源。
- 错误处理能力较弱:在隐式链接中,如果dll/so文件不存在、损坏或版本不兼容,程序可能会立即崩溃或显示错误消息,而无法像显式链接那样进行错误处理。
- 启动时间可能较长:如果程序依赖多个dll/so,并且这些dll/so在程序启动时都被加载了,那么程序的启动时间可能会相对较长。
实例:隐式链接
一般我们在使用某个库时的步骤:C++实现web后台 cgi 程序时,需要使用 libcgicc.so 动态库。
1)将动态库拷贝至/lib/下
2) 代码中包含要使用的库函数所对应的头文件
库的编译阶段:
1)使用-l来链接动态库:-lcgicc
2)-I 来指定库的头文件
arm-linux-gnueabihf-g++ -o myapp.cgi myapp.cpp -I /usr/local/cgicc-3.2.20/include -lcgicc
注意: 在代码中包含库头文件时,要和编译指定的库头文件路径相匹配。
这篇关于显式链接和隐式链接的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!