C++ 静态链接库与动态链接库

(一)定义

1. 静态库(.lib / .a)

函数和数据被编译进一个二进制文件,在编译可执行文件的时候,链接器从库(.lib/.a)中复制这些函数和数据并把他们和其他模块组合起来创建最终的可执行文件(.exe)。当发布程序时,只需可执行文件并不需要发布静态库

2. 动态库(.dll / .so)

函数所在的DLL文件文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供
一个引入库(.lib)文件(也称“导入库文件”)和一个DLL(.dll)文件。虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别,对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。

(二)区别和使用

1. 动态库的导入库静态库的区别

  • 动态库的导入库包含地址符号表等信息(属性-》配置属性-》链接器-》高级-》导入库中可设置生成的导入库名称)
  • 静态库包含实际执行代码、符号表等

2. 使用区别

1. 使用静态库

  • 需要的文件:.h 头文件、.lib静态库文件
  • 头文件:属性-》配置属性-》C/C+±》常规-》附加包含目录
  • .lib静态库目录:属性-》配置属性-》链接器-》常规-》附加库目录
  • .lib静态库文件名:属性-》配置属性-》链接器-》输入-》附加依赖项
  • 代码中手动引入静态库:pragma comment(lib,"xxx.lib")

2. 使用动态库

  • 需要文件 .h 头文件、 .lib 、 .dll 或者只是 .dll
  1. 动态调用/显示调用
  • 创建一个函数指针,其指针数据类型要与调用的 DLL 引出函数一致
  • 通过 Win32 API 函数LoadLibrary()显式的调用DLL,此函数返回 DLL 的实例句柄
  • 通过 Win32 API 函数GetProcAddress()获取要调用的DLL 的函数地 址,把结果赋给自定义函数的指针类型。
  • 使用函数指针来调用 DLL函数。
  • 最后调用完成后,通过 Win32 API 函数FreeLibrary()释放DLL 函数。

注意:显式调用的前提是使用者需要知道想调用的函数的名字、参数、返回值信息,也就是说虽然编译链接用不上.h头文件,但是调用者编程时可能还是要看.h文件作参考来知道函数名字、参数、返回值信息

  1. 静态调用/隐式调用
  • 同使用静态库一样引入 .h 、.lib
  • .dll 文件与可执行文件放到同一目录

(三)关键字 extern "C"

  1. 关于C++和C语言编译器生成目标文件的区别(生成的obj目标文件命名规则不同)
  • 由于C++支持多态性,在编译的时候,CPP编译器会将参数类型和函数名连接在一起,于是在程序编译成为目标文件以后,CPP编译器可以直接根据目标文件中的符号名将多个目标文件连接成一个目标文件或者可执行文件
  • 在C语言中,由于完全没有多态性的概念,C编译器在编译时除了会在函数名前面添加一个下划线之外,什么也不会做。

例如下面函数在C++ 和 C语言中编译目标文件时命名不同
int add(int a,int b);
C++: _add_int_int
C: _add
所以在使用 C 和 C++ 进行混合编程时,会造成编译器在程序链接阶段无法找到函数具体的实现,导致链接失败。
extern “C” 既可以修饰一句 C++ 代码,也可以修饰一段 C++ 代码,它的功能是让编译器以处理 C 语言代码的方式来处理修饰的 C++ 代码
常用场景代码示例:

#ifdef  __cplusplus
extern "C" {
#endif




#ifdef  __cplusplus
}
#endif  

(四)关键字 __declspec

  • __declspec是Microsoft VC中专用的关键字,它配合着一些属性可以对标准C/C++进行扩充。__declspec关键字应该出现在声明的前面。
  • __declspec(dllexport)用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等。
  • .def文件(模块定义文件)是包含一个或多个描述各种DLL属性的Module语句的文本文件。.def文件或__declspec(dllexport)都是将公共符号导入到应用程序或从DLL导出函数。如果不提供__declspec(dllexport)导出DLL函数,则DLL需要提供.def文件。
  • __declspec(dllimport)用于Windows中,从别的动态库中声明导入函数、类、对象等供本动态库或exe文件使用。当你需要使用DLL中的函数时,往往不需要显示地导入函数,编译器可自动完成。不使用__declspec(dllimport)也能正确编译代码,但使用__declspec(dllimport)使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于DLL中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨DLL边界的函数调用中。声明一个导入函数,是说这个函数是从别的DLL导入。一般用于使用某个DLL的exe中。

示例代码:

#ifndef FBC_LIBRARY_LIBRARY_HPP_
#define FBC_LIBRARY_LIBRARY_HPP_
 
// reference: http://geoffair.net/ms/declspec.htm
 
#ifdef _MSC_VER
	#ifdef FBC_STATIC
		#define FBC_API
	#elif defined FBC_EXPORT
		#define FBC_API __declspec(dllexport)
	#else
		#define FBC_API __declspec(dllimport)
	#endif
#endif
 
#ifdef __cplusplus
extern "C" {
#endif
 
FBC_API int library_add(int a, int b);
FBC_API int value;
 
#ifdef __cplusplus
}
#endif
 
template<typename T>
class FBC_API Simple {
public:
	Simple() = default;
	void Init(T a, T b);
	T Add() const;
 
private:
	T a, b;
};
 
 
#endif // FBC_LIBRARY_LIBRARY_HPP_

#include "library.hpp"
#include <iostream>
#include <string>
 
FBC_API int library_add(int a, int b)
{
	value = 11;
 
	fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
	return (a+b);
}
 
template<typename T>
void Simple<T>::Init(T a, T b)
{
	this->a = a;
	this->b = b;
}
 
template<typename T>
T Simple<T>::Add() const
{
	fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
	return (a + b);
}
 
template class Simple<int>;
template class Simple<std::string>;

#ifndef FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
#define FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
 
#include <library.hpp>
 
namespace test_library_ {
 
#ifdef __cplusplus
	extern "C" {
#endif
 
__declspec(dllimport) int library_add(int, int);
__declspec(dllimport) int value;
 
#ifdef __cplusplus
	}
#endif
 
int test_library_1();
int test_library_2();
 
} // namespace test_library_
 
#endif // FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_

#include "test_library.hpp"
#include <iostream>
#include <string>
 
#include <library.hpp>
 
namespace test_library_ {
 
int test_library_1()
{
	int a{ 4 }, b{ 5 }, c{ 0 };
 
	c = library_add(a, b);
	fprintf(stdout, "%d + %d = %d\n", a, b, c);
	fprintf(stdout, "value: %d\n", value);
 
	return 0;
}
 
int test_library_2()
{
	Simple<int> simple1;
	int a{ 4 }, b{ 5 }, c{ 0 };
 
	simple1.Init(a, b);
	c = simple1.Add();
	fprintf(stdout, "%d + %d = %d\n", a, b, c);
 
	Simple<std::string> simple2;
	std::string str1{ "csdn blog: " }, str2{ "http://blog.csdn.net/fengbingchun" }, str3;
 
	simple2.Init(str1, str2);
	str3 = simple2.Add();
	fprintf(stdout, "contents: %s\n", str3.c_str());
 
	return 0;
}
 
} // namespace test_library_

注意理解library.hpp中FBC_API的条件编译可以表示静态链接库接口、动态链接库导出接口、动态链接库导入接口,通过VS中预编译宏(配置属性->C/C+±>预处理器->预处理器定义)的定义来实现不同的功能

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的静态链接库动态链接库的生成方法略有不同。下面分别介绍: 1. 静态链接库(Static Library) 静态链接库是指在编译链接阶段将库的代码复制到可执行文件中,因此可执行文件不再依赖于库文件,可以单独运行。静态链接库的生成方法如下: - 编写库的源代码,生成库的目标文件(.o)。 - 将所有目标文件打包成静态链接库文件(.a)。 具体的步骤如下: 首先编译库的源代码,生成目标文件(.o): ``` g++ -c lib.cpp -o lib.o ``` 然后将目标文件打包成静态链接库文件(.a): ``` ar rcs lib.a lib.o ``` 其中,`ar`命令用于打包目标文件,`rcs`参数表示创建库文件并插入目标文件,`lib.a`为库文件名,`lib.o`为目标文件名。 2. 动态链接库(Dynamic Library) 动态链接库是指在程序运行时加载库文件,因此可执行文件依赖于库文件,需要和库文件一起运行。动态链接库的生成方法如下: - 编写库的源代码,生成库的目标文件(.o)。 - 将所有目标文件编译为共享对象文件(.so)。 具体的步骤如下: 首先编译库的源代码,生成目标文件(.o): ``` g++ -c -fPIC lib.cpp -o lib.o ``` 其中,`-fPIC`参数表示生成位置无关代码,是动态链接库的必备参数。 然后将目标文件编译为共享对象文件(.so): ``` g++ -shared -o lib.so lib.o ``` 其中,`-shared`参数表示生成共享对象文件,`lib.so`为共享对象文件名,`lib.o`为目标文件名。 以上就是C++中生成静态链接库动态链接库的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值