C++ library
前言
c++的中使用库通常需要两个文件,即头文件include
和库文件library
。
头文件include
是一个包含函数变量声明的头文件,在c++程序中必须包含头文件,才能获得库中函数、变量的声明,才能正常使用。头文件声明了哪些函数或变量可以使用。
库文件library
又分为静态库和动态库。
静态库会被编译到可执行文件中,优点是速度更快,因为在链接器链接的时候有更多的优化技术来优化静态链接;并且可执行程序可以直接运行;但是缺点是可执行程序体积较大,因为其不仅包含其自身的代码,还包含了库的内容。
动态库不会编译到可执行文件中,优点是目标程序体积小,并且较为灵活(如果需要修改相关功能代码,只需要重新编译动态库即可,可以在不重新编译可执行文件的情况下实现功能升级。在相当多的业务场景中,可执行文件在工作场景下没有编译条件。),缺点是程序在运行时必须要找到全部的动态库依赖才能正常工作。
动态库
动态库用作动态链接,其链接发生在程序运行时,
动态库在Windows系统下通常为x.dll文件和xd.lib文件。
依赖静态库的动态库
头文件只有函数的声明,lib文件包含了具体函数的地址,程序计数器PC只有知道函数的具体地址才能跳转执行。
通常(并不绝对),在动态库的调用过程中,会使用静态库。通常的文件命名方式是在文件名末尾加一个d或者dll来表明这是一个用于动态库的静态库。
还需要静态库通常有以下原因:
- LIB文件中包含了DLL的导入库信息。编译链接程序时,是通过LIB提供的导入库信息找到DLL中的导出函数。
- LIB中记录了DLL的接口信息,如函数名,参数等。这些信息是链接DLL时需要的。
- LIB中包含了一个跳转表,指向DLL中导出函数的地址。链接器需要这些地址信息来连接DLL。
- LIB中可能包含了一个启动加载器,用于加载和初始化DLL。
- LIB中可能包含了DLL的部分导出变量或资源,不需要每次都从DLL加载。
- 使用LIB还可以避免直接暴露DLL接口,一定程度上保护DLL代码。
只使用动态库
在Windows系统下,可以通过LoadLibrary或者GetProcAddress这两个Windows API函数动态加载DLL,并获取函数地址,不需要导入库信息。但需要注意,不使用LIB方式会使代码依赖具体的DLL,移植性差,也无法获取编译期类型检查。
_declspec(dllexport)与_declspec(dllimport)
在Windows平台上,dllexport和dllimport都是_declspec关键字的修饰符,用于控制DLL接口的导出和导入。
其通常是放在头文件中(用在函数或者变量的声明),
#ifdef _EXPORTS
#define _API __declspec(dllexport)
#else
#define _API __declspec(dllimport)
#endif
_declspec(dllexport)的作用是:
- 把该函数或变量的符号名称导出到DLL的导出表中。
- 在编译生成的LIB文件中,包含一个该符号名称的导入库信息。
- 这个导入库信息中包含了一个指向DLL中对应函数/变量的指针或跳转地址。
_declspec(dllimport)的作用是:
- 将某DLL中的函数导入到当前项目中使用。
- 它通常用在调用DLL的程序项目中,修饰从DLL中导入的函数。
- 编译器会根据此关键字生成导入库,帮助程序链接到DLL函数。
区别在于:
- dllexport负责导出DLL接口,dllimport负责导入DLL接口。
- dllexport和dllimport都会导出lib文件,但是区别在于dllexport发生在编译动态库时,dllimport发生在编译调用库的程序时,二者生成的lib文件并不完全相同。
_declspec(dllexport)
可以简单理解为:_declspec(dllexport)会把函数/变量编译到DLL的导出表中,在LIB文件中生成一个指向它的指针,方便其他程序链接导入。
_declspec(dllimport)
_declspec(dllimport)只是编译期的导入声明,最终加载DLL还需要额外的工作。
- 没有LIB - 必须使用_declspec(dllimport)生成导入库。
- 已经有LIB - 不能使用_declspec(dllimport),直接声明DLL符号。
- 同时有LIB和_declspec(dllimport) - 会链接错误。
_declspec(dllimport)是生成导入库的方式,而LIB文件已经包含导入库信息,二者不应同时出现,否则编译器将收到冲突的导入声明。
使用Windows API中的LoadLibrary和GetProcAddress函数动态加载DLL和获取函数地址时,不需要也不应该使用_declspec(dllimport)声明。LoadLibrary和GetProcAddress已经可以直接加载DLL并获取函数地址,不需要导入库。
静态库
静态库用作静态链接,其链接发生在程序编译时。
静态库在Windows系统下通常为x.lib文件。