在Windows中安装好的软件目录下会看到一大堆以lib,dll后缀的文件。而且有时候玩游戏或者运行软件的时候经常会遇到无法运行,因为缺少×××.dll,而这些就是软件运行需要的库,也就是源码编译后的文件。
一、程序中的库是什么
本质上说库是一种可执行的二进制文件,可以直接被操作系统载入内存执行。
二、库的种类
库有两种:静态库和动态库。Windows平台下的静态库以“.lib”为后缀名,动态库以“.dll”为结尾。Linux平台下的静态库以“.a”结尾,动态库以“.so”为结尾。
三、静态和动态库的不同
程序从源码到变成可执行程序需要经过四个过程:预编译,编译,汇编,链接。而静态和动态的主要区别在于最后的链接部分。
四、静态库的创建和使用
静态库的命名规则:名字分为三部分:前缀lib+库名字+后缀.a (注意:库的名字一般为小写)
为了方便,我们新建一个数学类:StaticMath.h 和 StaticMath.cpp
//StaticMath.h
#pragma once
class StaticMath
{
public:
StaticMath(void);
~StaticMath(void);
static double add(double a, double b);
void print();
};
//StaticMath.cpp
#include"StaticMath.h"
#include<iostream>
#include<stdlib.h>
using namespace std;
StaticMath::StaticMath()
{
cout<< "StaticMath(void) has been called"<<endl;
}
StaticMath::~StaticMath()
{
cout<<"~StaticMath(void) has been called"<<endl;
}
double StaticMath::add(double a, double b)
{
double c = a + b;
return c;
}
void StaticMath::print()
{
cout<<"print function has been called!"<<endl;
}
静态库的创建:
首先,将代码文件编译成目标文件.o(StaticMath.o)
- -static指定生成静态链接库(也可以省略);
g++ -static -c StaticMath.cpp
然后,通过ar工具将目标文件打包成.a静态库文件
ar -crv libstaticmath.a StaticMath.o
此时就生成了静态库libstaticmath.a。
然后在写一个main.cpp的测试调用文件:
//main.cpp
#include"StaticMath.h"
#include<iostream>
using namespace std;
int main(int argc, char* argv[])
{
double a = 10;
double b = 20;
cout<<"a + b = "<<StaticMath::add(a,b)<<endl;
StaticMath sm;
sm.print();
system("pause");
return 0;
}
调用命令大致分为三部分:编译器 命令 参数
g++ -o main main.cpp -L../StaticLibraryDir -l staticmath
- -L后面加的参数是静态库的存放文件夹,因为是当前目录,所以使用”.“代替"./StaticLibraryDir"即可;
- -l后面加的参数是静态库的名字(去掉前缀后的名字)。
最后生成的main程序,在当前目录下使用以下命令即可调用:
./main
五、动态库的创建和使用
为了方便,我们新建一个数学类:DynamicMath.h 和 DynamicMath.cpp
//DynamicMath.h
#pragma once
class DynamicMath
{
public:
DynamicMath(void);
~DynamicMath(void);
static double add(double a, double b);
void print();
};
//DynamicMath.cpp
#include"DynamicMath.h"
#include<iostream>
using namespace std;
DynamicMath::DynamicMath()
{
cout<< "DynamicMath(void) has been called"<<endl;
}
DynamicMath::~DynamicMath()
{
cout<<"~DynamicMath(void) has been called"<<endl;
}
double DynamicMath::add(double a, double b)
{
double c = a + b;
return c;
}
void DynamicMath::print()
{
cout<<"print function has been called!"<<endl;
}
动态库和静态库的不同在于,在于添加命令的时候需要加上-fPIC。-fPIC (位置无关代码Position-Independent Code)的使用,会生成 PIC 代码,.so 要求为 PIC,以达到动态链接的目的,否则,无法实现动态链接。
首先,生成目标文件,此时要加编译器选项-fPIC
g++ -fPIC -c DynamicMath.cpp
然后,生成动态库,此时要加链接器选项-shared
g++ -o libdynamicmath.so -shared DynamicMath.o
这样就生成了动态库,将main.cpp修改一下测试:
//main.cpp
#include"DynamicMath.h"
#include<iostream>
#include<stdlib.h>
using namespace std;
int main(int argc, char* argv[])
{
double a = 10;
double b = 20;
cout<<"a + b ="<<DynamicMath::add(a,b)<<endl;
DynamicMath Dm;
Dm.print();
system("pause");
return 0;
}
引用动态库编译成可执行文件(跟静态库方式一样):
g++ -o main main.cpp -L../DynamicLibraryDir -l dynamicmath
- -L后面加的参数是静态库的存放文件夹,因为是当前目录,所以使用”.“代替"./DynamicLibraryDir"即可;
- -l后面加的参数是静态库的名字(去掉前缀后的名字)。
但是运行此时生成的main不并能成功!因为系统无法定位到这个动态库,而不是程序发现不了。
方法一:(系统级)
将生成的.so文件拷贝到系统/usr/lib或者/lib文件夹下,再运行即可。
方法二:(临时办法)
在运行main前指定运行的库的路径为当前路径
LD_LIBRARY_PATH=. ./main
方法三:(makefile)
这个属于方法二的延伸,为的就是使其自动化一些,在makefile中加入后,就不用每次运行都写前缀了:
main.out: main.cpp DynamicMath.so
g++ main.cpp -L . -l dynamicmath
LD_LIBRARY_PATH=. ./main.out
运行结果就是:
六、静态库和动态库的区别
最要的区别在于资源的利用上:
但是理论上的区别有很多:
1、二者的不同点在于代码被载入的时刻不同。静态库在程序编译过程中会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。动态库在程序编译过程中并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。
2、静态库属于完整意义上的封装。动态库并不算完整意义上的封装,更侧重于共享。
七、在Linux下显式调用动态库
#include <dlfcn.h>,提供了下面几个接口:
- void * dlopen( const char * pathname, int mode ):函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。
- void* dlsym(void* handle,const char* symbol):dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。
- int dlclose (void *handle):dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
- const char *dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。