目录
动态库/静态库
首先, 我们先来看一下gcc的编译过程
1. 预处理 : 展开所有代码, 所有的宏定义, 所有的条件编译指令, 删除所有注释等
2. 编译 : 代码的语义语法纠错, 完毕之后生成汇编代码
3. 汇编 : 将汇编代码解释成为机器语言
4. 链接 : 将所有的.c文件目标代码链接在一起, 加上所使用的一些库, 生成可执行程序
概念
静态库 (.a) : 程序在编译链接的时候把库的代码链接到可执行文件中, 运行时不再需要
动态库 (.so) : 程序在运行时才链接动态库的代码, 动态库可以在多个程序间共享, 内存中加载一份, 节省磁盘空间, 所以也叫共享库
例子
我们先来看一个例子
我们在同一个目录下写两个文件
child.c
#include <stdio.h> void print() { printf("nihao\n"); }
main.c
#include <stdio.h> int main() { print(); return 0; }
我们想要在main.c中使用child.c中的函数print, 我们编译一下
这个时候很明显有一个警告, 显示print函数未定义
所以我们想让一个函数在另一个.c文件中使用我们就要写一个头文件, 将print函数声明一下, 然后将头文件包到两个.c文件中(略)
child.h
#include <stdio.h> void print();
然后再编译
这样就可以了
然后我们输入一个命令 : ldd main
我们可以看到, 有一个libc.so这个库, 这个就是我们的c库, 是一个动态库, 在这里进行了链接, 也就是说我们在生成main这个可执行程
序的时候不仅用到了main.o 和 child.o , 还用到了标准c库, 才生成了可执行程序.
那么下面我们就来看怎样自己生成库
生成库
这里注意, 库中不能包含main函数, 因为库是给别人去使用的, 别人会自己定义自己的main函数, 要不然会造成重定义
我们就以上面的child.o为例, 生成库
生成动态库
1. gcc -fPIC -c child.c -o child.o
-fPIC : 产生位置无关代码, 我们一个程序在链接动态库的时候, 这个库中的代码并没有写入到我们的文件中, 所以我们的程序很难记录库中函数的位置信息, 程序被加载到内存当中时代码都加载在代码端, 但是动态库相当于都是后续加载的, 运行时
才加载, 将动态库加载到内存中, 然后从内存中映射到虚拟地址空间, 映射的地址是不固定的, 所以我们生成动态库的时候就
要产生一个位置无关代码, 这个时候, 产生的代码位置都是相对的, 都是以偏移量计算的, 这时候当动态库加载到内存中, 映射
到虚拟地址空间时, 就会有一个首地址, 我们代码中只记录偏移量时多少, 等到真的从虚拟地址空间拿到首地址后加上偏移量
就可以找到实际的地址.
2. gcc --share child.o -o libmchild.so
--share : 指定gcc要生成动态库而不是可执行程序
lib是前缀, .so是后缀, 中间是自己的库名称
可以看到, 我们已经生成了一个动态库
我们来看一下这个动态库.so文件
我们可以看见有很多信息, 我们的print函数也在里面
生成静态库
1. gcc -c child.c -o child.o
2. ar -cr libmychild.a child.o
-c : 创建静态库
-r : 模块替换
lib是前缀, .a是后缀, 中间是自己的库名称
可以看到, 生成了一个.a文件
然后我们来看一下这个.a文件
可以看到, 里面只有一个child.o文件, 而且只有一个print函数
使用库
使用动态库
我们编译并且链接库, gcc 默认链接的是动态库, 所以这个地方会连接上动态库
-l : 指定链接的库名称
报错了, 这是因为链接库的时候, 需要将库放在指定路径下, 搜索库文件的时候是在指定目录下去找的.
一般就是以下几个路径:
/lib /lib64 /user/lib /user/lib64
解决方案:
1. 将库放到指定目录下----64位操作系统就是 /lib64, 这个简单, 就不用说了, 需要root权限
运行:
注意 : 运行的时候也需要这个.so文件在这个路径下, 要不然还是会出错
2. 设置环境变量-----不会设置的可以看我以前的博客进程概念
(1)库文件的链接搜索路径
(2)库文件的运行加载路径
运行:
3. 在gcc生成可执行程序时直接指定库的搜索路径
-L : 指定链接库的搜索路径
. : 当前路径
这里还有一个选项:
-I : 指定头文件的搜索路径
如果我们现在新建一个文件夹include, 将child.h放进去, 然后编译, 发现有错误, 找不到child.h
然后我们加上-I , 将child的路径加上, 然后运行, 发现就成功了
使用静态库
有一种写法是gcc 的时候加上-static选项
但是使用-static的时候, 这个时候c库也就是使用的静态链接, 但是通常我们不想静态链接c库, 因为这样的话会有代码冗余.
我们通常是只想让第三方库使用静态而不是所有的库, 所以我们采用下面的方法
将第三方静态库拷贝到指定路径下,使用 -L 选项指定库的链接搜索路径,那么这时候,链接的就是静态库.
1. 创建lib文件夹, 将libmchild.a放进去
2. 编译链接时使用-L选项指定搜索静态库搜索路径
运行 : 这个时候就好了.
所以我们如果要用到第三方静态库, 但是不想静态生成程序, 那么就拷贝静态库然后指定连接路径就好了
总结
动态库生成:命名:lib是前缀, .so是后缀,中间是库名称
gcc -FPIC -c child.c -o child.o
gcc --share child.o -o libmychild.so
静态库生成:命名:lib是前缀, .a是后缀,中间是库名称
gcc -c child.c -o child.o
ar -cr libmychild.a child.o
-c 创建 -r 模块替换
使用库:链接库的时候,库文件有默认的搜索路径/lib/
gcc main.c -o main -lmychild 报错:找不着库
1.将库文件放在指定位置下 /lib64 /lib /user/lib64 /user/lib
2.设置环境变量
库文件的链接搜索路径:LIBRARY_PATH=.
库文件的运行加载路径:LD_LIBRARY_PATH=.
3.在gcc升成可执行程序时,直接指定库的搜索路径
gcc main.c -o main -L . -lmychild
gcc -L 选项: 指定链接库的搜索路径
-I 选项: 指定头文件搜索路径