C++ library

前言

c++的中使用库通常需要两个文件,即头文件include和库文件library

头文件include是一个包含函数变量声明的头文件,在c++程序中必须包含头文件,才能获得库中函数、变量的声明,才能正常使用。头文件声明了哪些函数或变量可以使用。

库文件library又分为静态库动态库

静态库会被编译到可执行文件中,优点是速度更快,因为在链接器链接的时候有更多的优化技术来优化静态链接;并且可执行程序可以直接运行;但是缺点是可执行程序体积较大,因为其不仅包含其自身的代码,还包含了库的内容。

动态库不会编译到可执行文件中,优点是目标程序体积小,并且较为灵活(如果需要修改相关功能代码,只需要重新编译动态库即可,可以在不重新编译可执行文件的情况下实现功能升级。在相当多的业务场景中,可执行文件在工作场景下没有编译条件。),缺点是程序在运行时必须要找到全部的动态库依赖才能正常工作。

动态库

动态库用作动态链接,其链接发生在程序运行时,

动态库在Windows系统下通常为x.dll文件和xd.lib文件。

依赖静态库的动态库

头文件只有函数的声明,lib文件包含了具体函数的地址,程序计数器PC只有知道函数的具体地址才能跳转执行。

通常(并不绝对),在动态库的调用过程中,会使用静态库。通常的文件命名方式是在文件名末尾加一个d或者dll来表明这是一个用于动态库的静态库。

还需要静态库通常有以下原因:

  1. LIB文件中包含了DLL的导入库信息。编译链接程序时,是通过LIB提供的导入库信息找到DLL中的导出函数。
  2. LIB中记录了DLL的接口信息,如函数名,参数等。这些信息是链接DLL时需要的。
  3. LIB中包含了一个跳转表,指向DLL中导出函数的地址。链接器需要这些地址信息来连接DLL。
  4. LIB中可能包含了一个启动加载器,用于加载和初始化DLL。
  5. LIB中可能包含了DLL的部分导出变量或资源,不需要每次都从DLL加载。
  6. 使用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)的作用是:

_declspec(dllimport)的作用是:

区别在于:

_declspec(dllexport)

可以简单理解为:_declspec(dllexport)会把函数/变量编译到DLL的导出表中,在LIB文件中生成一个指向它的指针,方便其他程序链接导入。

_declspec(dllimport)

_declspec(dllimport)只是编译期的导入声明,最终加载DLL还需要额外的工作。

  1. 没有LIB - 必须使用_declspec(dllimport)生成导入库
  2. 已经有LIB - 不能使用_declspec(dllimport),直接声明DLL符号。
  3. 同时有LIB和_declspec(dllimport) - 会链接错误。

_declspec(dllimport)是生成导入库的方式,而LIB文件已经包含导入库信息,二者不应同时出现,否则编译器将收到冲突的导入声明。

使用Windows API中的LoadLibrary和GetProcAddress函数动态加载DLL和获取函数地址时,不需要也不应该使用_declspec(dllimport)声明。LoadLibrary和GetProcAddress已经可以直接加载DLL并获取函数地址,不需要导入库。

静态库

静态库用作静态链接,其链接发生在程序编译时。

静态库在Windows系统下通常为x.lib文件。