目录
1.动态库和静态库到底是什么
(1).静态库 vs 动态库
(2).动态链接和静态链接的优劣
(3).ldd指令
2.自己制作静态库
(1).打包静态库
(2).ar指令
3.如何用我们的库
4.自己制作动态库
(1).打包动态库
(2).动态库在运行的时候也要给出库的路径
1.动态库和静态库到底是什么
首先我们需要知道什么是动静态库
1.一般库分为两种:静态库和动态库
在Linux中,
- 如果是动态库:库文件是以.so作为后缀的
- 如果是静态库:库文件是以.a作为后缀的
2.库文件的命名:libXXXX.so... or libYYYY.a...
库的真实名字:去掉lib前缀,去掉.a...,so...后缀,剩下的就是库名称!
3.c++文件的后缀可以是.cpp .cc .cxx
换成别的后缀名就没办法通过编译
(1).静态库 vs 动态库
静态库:.a (windows中是.lib)
动态库:.so (windows中是.dll)
- 静态链接是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”
- 动态链接与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时(需要用到库中的内容时)由运行时的链接文件加载库,多个程序共享使用库的代码。这样可以节省系统的开销。动态库一般后缀名为“.so”
gcc 在编译时默认使用动态库,使用file指令可以看出来
[zebra@VM-8-12-centos test6]$ file test6
test6: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c27bd79ff83896bc7afa2c4b8418a0c308d663ab, not stripped
[zebra@VM-8-12-centos test6]$
(2).动态链接和静态链接的优劣
指定test.c使用静态链接的方式链接库(gcc默认采用动态链接)
$ gcc hello.c -o h_static -static
用file命令可以看到确实是采用了静态链接
[zebra@VM-8-12-centos test6]$ g++ -o test6_static test6.cpp -std=c++11 -static
[zebra@VM-8-12-centos test6]$ ll
total 1608
-rwxrwxr-x 1 zebra zebra 9112 Nov 22 19:49 a.out
-rw-rw-r-- 1 zebra zebra 73 Nov 4 16:30 Makefile
-rwxrwxr-x 1 zebra zebra 8976 Nov 11 16:23 test6
-rw-rw-r-- 1 zebra zebra 249 Nov 15 19:53 test6.cpp
-rwxrwxr-x 1 zebra zebra 1612816 Nov 22 19:50 test6_static
[zebra@VM-8-12-centos test6]$ file test6_static
test6_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=eaeab76d92e3a8c6622b3e706dda1f7688b99132, not stripped
[zebra@VM-8-12-centos test6]$
我们发现静态链接生成的可执行文件比动态链接大很多。
动态链接的优劣:节省内存和硬盘的空间,下载传输比较方便。(库没了就运行不了)
静态链接的优劣:可移植性强,移植的时候不需要将库移植过去。(会比较占空间)
动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
(3).ldd指令
ldd 可执行程序
可以查看当前程序用了哪些动态库
[zebra@VM-8-12-centos test6]$ ldd test6
linux-vdso.so.1 => (0x00007ffeb0589000)
libstdc++.so.6 => /home/zebra/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6 (0x00007fc8a81d3000)
libm.so.6 => /lib64/libm.so.6 (0x00007fc8a7ed1000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc8a7cbb000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc8a78ed000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc8a8554000)
[zebra@VM-8-12-centos test6]$
2.自己制作静态库
我们vim打开libc库文件发现是乱码,因为库本身就是二进制文件,那么我们如何得知一个库给我们提供了什么方法?
一套完整的库:1.库文件本身。2.头文件(是文本形式的,会说明库中暴露出来的方法的基本使用)。3.说明文档
Q:我们在C/C++中,为什么有时候写代码的时候,有时候是.h里面放上声明,.c/.cpp放入实现?为什么要这么设计呢? ?
因为我们要制作库!,更加方便使用。之后给别人用的时候,库文件和头文件都给,这样库文件就可以封装起来(转换成二进制文件)了。(毕竟是自己写出来的代码,不想给别人看,只想给别人用,那就转换成二进制文件)
如果是要开源的代码,写在同一个文件里面,后缀名可以是.hpp
头文件第一行:#pragma once:为了避免同一个文件被include多次
如果我们没有打包成库,.h文件(include的时候要写正确路径,或者借用环境)和.cc文件是分离的,此时编译的时候需要把.cc文件也要带上,否则会报错
如:
[zebra@VM-8-12-centos test12_libtest]$ g++ -o test test.cc ./test_lib/add.cc ./test_lib/mul.cc
[zebra@VM-8-12-centos test12_libtest]$ ll
total 24
-rw-rw-r-- 1 zebra zebra 69 Nov 22 20:14 Makefile
-rwxrwxr-x 1 zebra zebra 9048 Nov 22 20:24 test
-rw-rw-r-- 1 zebra zebra 245 Nov 22 20:24 test.cc
drwxrwxr-x 2 zebra zebra 4096 Nov 22 20:16 test_lib
[zebra@VM-8-12-centos test12_libtest]$
(1).打包静态库
1.将所有.c源文件文件编译成.o
2.使用ar命令将所有.o文件打包在一起,这就是一个库了
(2).ar指令
ar是gnu归档工具,rc表示(replace and create),类似tar,zip
生成静态库
[zebra@VM-8-12-centos test_lib]$ ar -rc test_lib.a add.o mul.o(这里使用了$@ $^)
$@表示目标
$^表示所有的依赖
$<表示第一个依赖
%.o表示所有以.o结尾的文件(Makefile中特有的通配符)
使用Makefile生成test_lib.a
[zebra@VM-8-12-centos test_lib]$ make
g++ -c -o add.o add.cc
g++ -c -o mul.o mul.cc
ar -rc test_lib.a add.o mul.o
[zebra@VM-8-12-centos test_lib]$ ll
total 32
-rw-rw-r-- 1 zebra zebra 59 Nov 22 20:24 add.cc
-rw-rw-r-- 1 zebra zebra 82 Nov 22 20:10 add.h
-rw-rw-r-- 1 zebra zebra 1248 Nov 22 20:43 add.o
-rw-rw-r-- 1 zebra zebra 192 Nov 22 20:43 Makefile
-rw-rw-r-- 1 zebra zebra 60 Nov 22 20:24 mul.cc
-rw-rw-r-- 1 zebra zebra 35 Nov 22 20:07 mul.h
-rw-rw-r-- 1 zebra zebra 1248 Nov 22 20:43 mul.o
-rw-rw-r-- 1 zebra zebra 2714 Nov 22 20:43 test_lib.a
查看静态库中的目录列表
[zebra@VM-8-12-centos test_lib]$ ar -tv test_lib.a
rw-rw-r-- 1001/1001 1248 Nov 22 20:43 2022 add.o
rw-rw-r-- 1001/1001 1248 Nov 22 20:43 2022 mul.o
t:列出静态库中的文件
v:verbose 详细信息
3.所以静态库的本质就是把所有的.c源文件编译成.o,然后打包放到一个.a文件中
4.我们还可以加两个指令,output和install分别用来打包生成库文件和头文件,或者安装(这样使用头文件和库文件就不需要加上路径了,但是只有本次启动服务器有效,除非修改配置文件)
注意一下这里库的名字,一定要是lib+名称,否则后面链接库的时候会出错!!!
3.如何用我们的库
使用指令g++ -o test test.cc -std=c++11 -I ./output_lib -L ./output_lib -ltest
注意:
1.如果include里面已经指明头文件路径,这里可以不加这个选项
2.库名称必须是lib+名字(可以跟上.a后缀),这里链接库的时候把lib去掉,变成-l+去掉lib后的库名称(实际上库的真实名字就应该去掉lib)
#include<iostream>
#include"./output_lib/add.h"
#include"./output_lib/mul.h"
using namespace std;
int main()
{
int a = 3;
int b = 4;
int sum = add(a,b);
int multi = mul(a,b);
cout << sum << endl << multi << endl;
}
[zebra@VM-8-12-centos test12_libtest]$ make
g++ -o test test.cc -std=c++11 -I ./output_lib -L ./output_lib -ltest
4.自己制作动态库
(1).打包动态库
和动态库不同的是,生成.o文件的时候,需要加上-fPIC选项;生成库文件的时候,gcc加上-shared选项,而不需要使用ar指令了。
libtest.so:add.o mul.o
# 动态库使用的不是ar指令,而是使用g++指令,加上-shared选项
g++ -shared -o $@ $^
# 动态库在生成.o文件的时候,需要加上-fPIC选项
%.o:%.cc
g++ -fPIC -c -o $@ $^
# 和静态库一样
.PHONY:clean
clean:
rm -rf *.o libtest.so output_lib_dynamic
# 和静态库一样
.PHONY:output
output:
mkdir output_lib_dynamic
cp -rf *.h output_lib_dynamic
cp -rf libtest.so output_lib_dynamic
# 和静态库一样
.PHONY:install
install:
cp *.h /usr/include
cp libtest.so /lib64
(2).动态库在运行的时候也要给出库的路径
静态库在运行的时候不需要找库,所以运行的时候直接./mytest就行了,在编译的时候需要指定。
动态库在运行的时候需要找库,所以除了在编译期间告诉编译器库在哪里,在运行期间也需要告诉加载器库在哪里
g++编译只是告知编译器头文件库路径在哪里,当程序编译好的时候,此时已经和编译无关了!
解决方法:
使用环境变量LD_LIBARARY_PATH(针对动态库)
//先获取当前库文件所在路径
[zebra@VM-8-12-centos output_lib_dynamic]$ pwd
/home/zebra/cpp/test/test12_libtest/output_lib_dynamic
//然后修改环境变量
[zebra@VM-8-12-centos output_lib_dynamic]$ export
LD_LIBRARY_PATH=/home/zebra/cpp/test/test12_libtest/output_lib_dynamic
注意:这个环境变量在下次添加的时候就会没掉。
当然,也可以把库文件和头文件放到系统默认的目录下,或者更新配置文件,不过最好不要这么做。