1、多个源程序联合编译
g++ a.cpp b.cpp ... -o ***
一次性编译所有.cpp源码,自动完成编译 + 链接,直接生成可执行文件 app。
1.1 执行流程:
分别把 a.cpp、b.cpp 编译汇编成临时 .o
自动调用链接器 ld,合并所有目标文件、系统库,生成可执行程序 app
适用场景
小型项目、单个命令就能跑完,不需要复用目标文件。

在上述案例中,主程序要是有的源程序代码,在add.cpp中,如果项目结束后,到了交付阶段,由
于主程序的生成需要其他程序联合编译,那么就要将源程序打包一起发给老板,这样该程序的开发者自
身的价值就不大了,该项目的知识产权就很容易被窃取。为了保护我们的知识产权,我们引入了库的概
念
2、什么是库
2.1 概念
库在linux中是一个二进制文件,它是由 .cpp文件(不包含main函数)编译而来,其他程序如果想
要使用该源文件中的函数时,只需在编译生成可执行程序时,链接上该源文件生成的库文件即可。库中
存储的是二进制文件,不容易被窃取知识产权,做到了保护作用。
2.2 库在linux系统中分为静态库和动态库
(1)windows:
***.lib:静态库
***.dll: 动态库
(2)linux:
***.a:静态库
***.so: 动态库
3、静态库及其制作(lib开头,.a结尾)
3.1 概念:
将一个***.cpp的文件编译生成一个 lib***.a的二进制文件,当你需要使用该源文件中的函数时,
只需要链接该库即可,后期可以直接调用
静态体现在:
在使用g++编译程序时,会将你的文件和库最终生成一个可执行程序(把静态库也放入到可执行
程序中),每个可执行程序单独拥有一个静态库,体积较大,但是,执行效率较高
编译到使用静态库的步骤:
源文件生成.o-->打包静态库-->使用静态库
3.2 编译生成静态库
g++ -c ***.cpp -o ***.o
//只编译不链接生成二进制文件(main 主函数的 cpp可以不建议单独执行第一步生成.o)
ar -crs lib***.a ***.o
//编译生成静态库(库名是lib后,.a之前的部分)
如果有多个.o文件共同编译生成静态库可以使用 :ar -crs lib***.a ***.o xxx.o ...
ar:用于生成静态库的指令
c:用于创建静态库
r:将文件插入或者替换静态库中同名文件
s:重置金泰克索引

3.3 使用静态库
g++ main.cpp -L 库的路径 -l库名 -I 头文件的名字 -o 文件名(-o后面可以不写)
注意:
头文件的时大写的i,库名是小写的L(其他同理)

.表示当前路径,库名后面没有空格
..表示上级目录

分文件夹多路径编译(头文件,源文件,静态文件都各自管理在一个文件夹里)
4.4 批量生成.o怎么做
每个.cpp单独执行 -c 编译,互不干扰
g++ -c 支持一次性传入多个 cpp,自动按文件名生成同名.o,不用手动写 -o
# 写法1:指定全部cpp文件
g++ -c func1.cpp func2.cpp calc.cpp
# 写法2:通配符匹配当前目录所有cpp(最省事)
g++ -c *.cpp
自动生成:func1.o、func2.o、calc.o,不需要加 -o 参数。
注意:
这种批量写法无法自定义.o输出名,只能和 cpp 同名。
4.5 批量生成.a怎么做
把全部.o打包进静态库
ar -crs libmylib.a *.o
4、动态库及其制作
4.1 概念:
将一个***.cpp的文件编译生成一个 lib***.so的二进制文件,当你需要使用该源文件中的函数时,
只需要链接该库即可,后期可以直接调用
动态体现在:
在使用g++编译程序时,会将你的文件和库中的相关函数的索引表一起生成一个可执行程序,每个可执行程序只拥有函数的索引表,当程序执行到对应函数时,会根据索引表,动态寻找相关库所在位置进行调用,体积较小,执行效率较低,但是可以多个程序共享同一个动态库,所以,动态库也叫共享库
4.2 编译生成动态库
g++ -fPIC -c ***.cpp -o ***.o
//编译生成二进制文件
g++ -shared ***.o -o lib***.so
//依赖于二进制文件生成一个动态库
上述两个指令可以合成一个指令
g++ -fPIC -shared ***.cpp -o lib***.so
4.3 使用动态库
gcc main.cpp -L 库的路径 -l库名 -I 头文件的名字 -o 文件名(-o后面可以不写)此时还是报错

4.4 解决(包含优缺点,适合场景):
方式1:更改路径的宏
export LD_LIBRARY_PATH=库的路径

(1) 优点
不用重新编译,现成的可执行文件临时救急;
(2) 缺点
只在当前终端窗口生效,关窗口 / 新开终端就失效,每次运行都要重复输两行命令。
(3) 适配环境:
临时测试、快速验证库是否正常,不适合长期使用。
方法2:将自己的动态库放入到系统的库函数目录中 (/lib64 /usr/lib64)
mv lib***.so /lib
(1) 优点
系统全局永久生效,所有程序都能自动找到这个 so;
(2) 缺点
需要 root 权限;
污染系统动态链接缓存,项目挪走后残留无效库路径;
多项目时容易出现库版本冲突。
(3) 适配环境
自己写的公共工具库、长期安装在服务器的软件,不适合你当前课堂分目录练习。
方式3:编译加 -Wl,-rpath(工程开发 / 学习练习最建议)
g++ main.cpp -L 库目录 -l库名 -I 头文件目录 -Wl,-rpath=库目录 -o 文件名
注意:
头文件的时大写的i,库名是小写的L,-W后面的是小写的L(其他同理)
(1) 优点
一次编译,到处运行:可执行文件内部自带库路径,新开终端、重启系统都不用额外输命令,直接 ./run;
相对路径 ../lib 跟随项目文件夹,整个项目文件夹复制到别的机器也能正常跑;
不修改系统全局配置,无权限污染,普通用户、root 都能用。
(2) 缺点
仅当前编译出的程序生效,别的程序不受影响。
(3) 适合:
.课堂练习、个人项目、小型交付程序
5. 动/静态库编译生成可执行程序的大小比较

只有不几个文件,就差了24字节的大小
项目不用交再交源程序,直接交编译生成的动态库/静态库发给上级即可
运行指令的具体含义
gcc main.cpp -L 库的路径 -l库名 -I 头文件的名字的具体含义
单纯说链接库:指 -L xxx -lxxx 这一段;剩下的是编译
整条完整命令:编译源代码 + 链接外部库 ,是完整的编译链接命令。
6 如何使用第三方库
使用数学库:#include<math.h>
l后面是库名

线程支持类库:#include<pthread.h>
如果man手册不能支持线程库:sudo yum install manpages-posix manpages-posix-dev

怎么知道咱们要用的头文件他对应的要链接 的名字?
查找这个头文件底下的的某个函数名
man 函数名
你可以看到SYNOPSIS里面的东西
比如线程库函数能看到:Compole with -pthread(相当于-lpthread)