Linux基础IO(十)之静态库(后续出动态库)
文章目录
- 静态库
- 库搜索路径
- 生成静态库
- 使用静态库
- 结论
静态库
静态库(.a
):程序在编译链接的时候把库的代码链接到可执行文件中。
程序运行的时候将不再需要静态库。
动态库(.so
):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,
而不是外部函数所在目标文件的整个机器码
在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,
这个过程称为动态链接(dynamic linking)
Linux
默认是动态库。
动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。
操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,
节省了内存和磁盘空间。
库搜索路径
从左到右搜索-L指定的目录。
由环境变量指定的目录 (LIBRARY_PATH)
由系统指定的目录
/usr/lib
/usr/local/lib
生成静态库
1、拷贝.so
文件到系统共享库路径下, 一般指/usr/lib
2、更改 LD_LIBRARY_PATH
[root@localhost linux]# lsadd.c add.h main.c sub.c sub.h[root@localhost linux]# gcc -c add.c -o add.o[root@localhost linux]# gcc -c sub.c -o sub.o
生成静态库
[root@localhost linux]# ar -rc libmymath.a add.o sub.o
ar
是gnu
归档工具,rc
表示(replace and create
)
查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.orw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.ot:列出静态库中的文件v:verbose 详细信息[root@localhost linux]# gcc main.c -L. -lmymath-L 指定库路径-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行。
3、ldconfig
配置/etc/ld.so.conf.d/
,ldconfig
更新
mymath.h
#pragma once#include <stdio.h>extern int myerrno;int add(int x,int y);
int sub(int x,int y);
int mul(int x,int y);
int div(int x,int y);
mymath.c
#include "mymath.h"int myerrno=0;int add(int x,int y)
{return x+y;
}
int sub(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){myerrno=1;return -1;}return x/y;
}
我们提供的方法给别人使用:
1.直接把源文件给他
2.把我们的源代码想办法打包成库。提供(库+.h
)也就是库+头文件。
必须要给头文件,头文件就相当于方法(库文件)的一份说明书。
静态库原理:
makeflie
lib=libmymath.a$(lib):mymath.oar -rc $@ $^
mymath.o:mymath.cgcc -c $^
.PHONY:clean
clean:rm -rf *.o *.a lib.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mymathlibcp *.h lib/includecp *.a lib/mymathlib
只有 $^
就是把源文件形成一个 .o
文件
使用静态库
在test里创建main.c
#include "mymath.h"int main()
{printf("1+1=%d\n",add(1,1));return 0;
}
编译时先要进行预处理,展开头文件,而展开头文件就需要先把头文件找到。
gcc
编译的时候找math.h
会在默认路径(/usr/include/
) 或者 当前目录(同级目录) 下找。
解决找头文件路径问题: -I
到指定目录下找头文件 或者 直接在头文件上加上绝对/相对路径
#include "lib/include/mymath.h"
找不到add方法,属于链接报错。(一般.o
之类的都属于链接报错)
因为.o
文件可以成功生成,所以前三个阶段没问题。
报错原因:找不到对应方法的实现,实现方法在库里面,
找不到静态库,因为只会在默认路径下/当前目录下找对应的静态库。
解决链接报错:-L
+ 静态库的路径(一定要指定链接哪个库-l
)
没有指定链接哪个库,就会报错
因为指定路径的目录下可能不仅仅只有一个静态库,
所以要显性告诉gcc
要链接哪个库。
在使用 -I
的时候不需要指明头文件,因为源代码里已经指明。
但是需要指明需要链接哪个静态库。
-l
后面要跟库的名字!!!(库的名字是去掉lib
,去掉.a
剩下的字符串!!!)
而不是整个字符串!!!
c/c++不用带 -I -L -l
是因为
1.头文件默认有
2.路径默认 gcc
能找到
3.因为纯c/c++代码,gcc/g++
就能认识c/c++
的静态库/动态库
把加->除,并且被除数是0
#include "mymath.h"int main(){//printf("10/0=%d,myerror=%d\n",div(10,0),myerrno);//形参实例化是右→左,所以在执行div之前,myerrno就被赋值为0了int n=div(10,0);printf("10/0=%d,myerror=%d\n",n,myerrno);return 0; }
如果不想用这么长、这么麻烦的指令:
1.把头文件拷贝到系统的 include
里/库文件拷贝到 lib64
目录下
这样只要指明-l
就可以编译通过。
两个 sudo
-> 库的安装。
(所谓的安装本质:头文件、库文件拷贝到系统的路径下)
卸载就是把头文件和库文件在特定目录下删除。
2.可以对目录里的头文件建立软链接,把软链接放到include
中
/把目录里的库也建立软链接,放到 lib64
的目录下,
把软链接放到系统默认的路径下也可以不用带这么多选项了。
#include "myinc/mymath.h"
结论
1.第三方库,往后在使用的时候,必定要使用 gcc -l
2.深刻理解 errno
的本质
(想让用户知道程序出错了,更想让用户知道程序错哪了)
3.-static
是建议型选项,如果动静态库都有,使用动态链接就默认,使用静态链接就加 -static
。
gcc
默认是动态链接,但是只提供静态库,所以gcc
只能进行静态链接。
(可执行程序可以有两种链接)
4.如果系统中需要连接多个库,则gcc
可以连接多个库,-l……