编译一个程序为可执行文件时,需要经过四部曲 : 预处理、编译、汇编、链接
而库是二进制形式目标模块的包,是多个目标模块(xxx.o)的集合
当使用库时,在链接阶段可以将库链接进可执行程序,同时也可以链接目标模块
库的概念
库是一个二进制文件,包含的代码可被程序调用(标准C库、数学库、线程库…) 库有源码,可下载后编译;也可以直接安装二进制包。 系统中一般默认库的安装路径:/lib 、 /usr/lib
库是事先编译好的,可以复用的代码。 在OS上运行的程序基本上都要使用库,使用库可以提高开发效率。 Windows和Linux下库文件的格式不兼容 Linux下包含静态库和共享库(动态库)
静态库(*.a)
特点
- 在链接阶段被加载到目标文件中
- 程序运行时不需要加载静态库,运行速度快
- 编译后的可执行程序体积较大
- 静态库升级后,程序需要重新编译链接
制作
#include <stdio.h>
void hello(void){
printf("hello world\n");
return ;
}
使用
#include <stdio.h>
void hello(void);
int main(){
hello();
return 0;
}
- 编译test.c 并链接静态库libhello.a
gcc -o test test.c -L. -lhello 注:-l表示链接哪个库名,-L表示库的额外搜索路径(相对路径或者绝对路径),例如在当前目录下时用“-L.”表示,在当前目录下的lib文件夹中用“-L./lib”
共享(动态)库(*.so)
特点
编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码
- 程序不包含库中代码,尺寸小
- 多个程序可共享同一个库
- 程序运行时需要加载库
- 库升级方便,无需要重新编译程序
制作
-
确定库中函数的功能接口 -
编译库源码hello.c bye.c
#include <stdio.h>
void hello(void)
{
printf("hello world\n");
return ;
}
#include <stdio.h>
void bye(void)
{
printf("bye!\n");
return ;
}
-
编译生成目标文件 gcc -c -fPIC hello.c bye.c -Wall(-fPIC:必须编译选项,表示生成位置无关代码 概念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。 ) -
创建共享库 common gcc -shared -o libcommon.so.1 hello.o bye.o -
为共享库文件创建链接文件 ln -s libcommon.so.1 libcommon.so -
符号链接文件命名规则 lib<库名>.so
使用
#include <stdio.h>
#include "common.h"
int main(){
hello();
bye();
return 0;
}
- 编译test.c 并链接共享库libcommon.so
gcc -o test test.c -L. -lcommon 注:-l表示链接哪个库名,-L表示库的额外搜索路径(相对路径或者绝对路径),例如在当前目录下时用“-L.”表示,在当前目录下的lib文件夹中用“-L./lib”
注意??:当静态库和动态库同名,优先使用动态库 使用动态库时,分两种加载方法,分别是静态加载和动态加载 静态加载就是程序一运行, 就自动加载 动态加载就是程序运行中,由程序猿的代码控制加载
此时还不能立即,因为在动态函数库使用时,会查找/usr/lib /lib目录下的动态函数库,而此时我们生成的库不在里边,我们要加载共享库。
加载共享库
添加共享库的加载路径 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
如何找到共享库
运行时要让执行程序顺利找到动态库,有三种方法 :
-
把库拷贝到/usr/lib和/lib目录下(不建议) -
在LD_LIBRARY_PATH环境变量中加上库所在路径。 //对当前的shell,临时生效 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. -
修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行sudo ldconfig刷新。 /etc/ld.so.conf是非常重要的一个目录,里面存放的是链接器和加载器搜索共享库时要检查的目录,默认是从/usr/lib /lib中读取的,所以想要顺利运行,我们也可以把我们库的目录加入到这个文件中并执行/sbin/ldconfig。
|