前言
在初学Linux的时候,简单的提到过动静态库,但当时只是简单的讲述了一下什么是动态库,什么是静态库,我们可以在此基础上更进一步---制作库。在使用gcc编译器的时候,是默认使用动态链接的。
静态库
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静
态库。
我们可以先自己设计一个库,只是制作一个简单的小程序,当作一个库,进行演示。
#pragma once
#include <iostream>
int add(int x, int y);
int mul(int x, int y);
int div(int x, int y);
#include "mymath.h"
int add(int x, int y)
{
return x + y;
}
int mul (int x, int y)
{
return x * y;
}
int div (int x,int y)
{
if (y == 0) {
std::perror("0");
return -1;
}
return x / y;
}
一个简单的计算+*/的小程序。如果有人想用我写的这个小程序, 1. 把源文件直接给他 2. 把我们的源代码想办法打包成库。
我们采取第二种方法,想办法将源代码打包成库。
touch Makefile创建一个Makefile文件,然后将.c文件生成.o文件,如果.o文件特别多,可以将.o文件打个包命名为libXXX.a---所有.o文件的集合,在编译的时候,main.c变成.o的时候,与静态库一结合,就能运行了。
lib=libmymath.a
$(lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.cc
g++ -c $^
.PHONY:clean
clean:
rm -f *.h *.a
生成静态库:ar -rc libxxx.a xxx.o xxx.o | ar是gnu归档工具,rc表示(replace and create)
一个简单的静态库,我们已经制作完成了,那么怎么将他发布呢?
lib=libmymath.a
$(lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.cc
g++ -c $^
.PHONY:clean
clean:
rm -f *.h *.a lib
.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
这样就可以了。未来别人要是想用我的库,只需要把lib文件夹给他就行了。
#include "lib/include/mymath.h"
int main()
{
std::cout << add(1, 1) << std::endl;
return 0;
}
然后就可以使用自己所创建的静态库写代码了,但是在使用库的时候,头文件可不是这样写的,而是直接写mymath.h
#include "mymath.h"
int main()
{
std::cout << add(1, 1) << std::endl;
return 0;
}
但是,当你编译这个文件的时候,却会出现错误,这是因为在编译的时候,有一个预处理阶段,这个阶段会进行头文件展开这个工作,再展开的时候,会默认去系统指定的目录和当前目录进行搜索。
但是系统里并不存在我刚刚写的小程序。lib在我当前的目录中,
系统不会自动的去当前目录的lib中去搜索,所以在编译的时候,我们可以加一个 -I 选项(-I 头文件路径),告诉系统在编译的时候去指定的目录寻找。
此时还是有问题,但问题已经不再是头文件找不到了,而是main函数中的add函数没找到。根本原因是因为找不到add函数的实现。但是我们已经把add的实现放在了libmymath.a里面吗?报错是报找不到add的实现,这就是因为找不到静态库,这不跟上面的问题一样吗?系统搜索库,会去系统中和当前目录中搜索,我自己实现的这个库是找不到的,这个情况下可以在编译的时候加上 -L选项(-L 静态库路径)
还是没找到。这是因为 ./lib/mymathlib/ 下的静态库可能有多个,系统不会自动的去寻找对应的库。所以还需要一个 -l选项(-l库名字)
库的名字是:去掉lib,去掉.a之后才是库的名字。
在使用第三方库的时候,就需要使用这样的方法。
动态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
现在我创建了四个文件 mylog.h和myloc.cc,myprintf.h和myprintf.cc文件,这两个.cc文件中都提供了两个简单的函数。
// mylog.h
#pragma once
#include <cstdio>
void Log(const char* str);
#include "mylog.h"
void Log(const char* str)
{
printf("mylog.cc :: %s\n", str);
}
// myprint.h
#pragma once
#include <cstdio>
void print();
#include "myprint.h"
void print()
{
printf("hello world"\n);
}
上面就是新建的头文件和源文件的代码实现,现在将这几个文件创建成一个动态库,其实思路跟静态库非常相似,要制作库,得先把源文件编译成.o文件。然后再将.o文件打包就行了。
在我们编译的时候,要加上一个fPIC(产生位置无关码)选项,下面会说。
在编译静态库的时候,通过ar -rc将.o文件打包形成一个静态库,静态库在形成的时候,g++不需要加任何选项,在形成动态库的时候,要先把文件形成.o。
这样就把刚才新增的文件变成.o文件了。
下一步就可以把.o文件打包了,ar是专门用来打包静态库的。要形成动态库,我们继续使用g++,因为g++是默认使用动态库的。
通过 g++ -shared( shared: 表示生成共享库格式 ) -o libmymethod.so *.-o可以把所有.o文件打包形成一个名为libmymethod.so的静态库。
制作的这个动态库其实是有X权限的,因为动态库也是一个可执行文件,但是他不能单独执行,需要别人来调用他。
这就是手动打包的过程。
dy-lib=libmymethod.so
static-lib=libmymath.a
.PHONY:all
all:$(dy-lib) $(static-lib)
$(static-lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.cc
g++ -c $^
$(dy-lib):mylog.o myprint.o
g++ -shared -o $@ $^
mylog.o:mylog.cc
g++ -fPIC -c $^
myprint.o:myprint.cc
g++ -fPIC -c $^
.PHONY:clean
clean:
rm -f *.a mylib *.so *.o
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mtlib/lib
cp *.so mylib/lib
这是一个makefile文件,带static的是静态库,带dy的是动态库。
这样就可以将我自己的库给别人用了。
但是,直接编译.cc文件的话,还是不能编译的,需要加上-I -L -l这三个选项。
解决加载找不到动态库的方法:
- 拷贝到系统默认的库路径 /lib64
- 在系统默认的库路径下建立软链接
- 将自己的库所在的路径,添加到系统的环境变量中 LD_LIBRARY_PATH中
- /etc/ld.so.conf.d 建立自己的动态库路径的配置文件,然后重新ldconfig即可