链接
g++/gcc编译选项
cpp生成过程
-o不仅可以指定输出的可执行文件,还可以指定中间文件的输出
demo.cpp
#include <iostream> using namespace std; int main(){ cout << "hello,world" << endl; return 0; }
-E
对源文件进行预处理,预处理后生成.i文件
-S
只进行预处理和编译,结果就是将C++代码中转译为汇编代码,生成.s汇编文件
-c
只进行
预处理, 编译,汇编
操作,生成.o 文件,不进行链接
重要编译参数
-D参数用来指定一个宏,宏和-D加不加空格都可以
#include <iostream> using namespace std; int main(){ #ifdef AAA cout << "In AAA area" << endl; #endif #ifdef BBB cout << "In BBB area" << endl; #endif cout << "hello,world" << endl; return 0; }
-M参数可以输出#incude的文件依赖
#include <cmath> int main(){ return 0; }
g++ -MD demo.cpp
-MD参数会将结果生成.d文件
g++ -MD demo.cpp -o mm.d
—可以指定文件名
GDB简单使用
要想程序能够被gdb调试,必须在编译时加上调试信息,也即是加上-g
选项
一般来说,不加-g也可以被调试
#include <iostream> int main(){ std::cout << "hello,world" << std::endl; return 0; }
生成可调式的文件
g++ -g demo.cpp -o exec
调试可执行文件:exec
gdb exec
回车后,即可输入入命令:
Reading symbols from exec...
(gdb)
- l或者list可以查看源码
- b或者break可以打断点
b 函数名 b 行号 b 文件名:行号 b 行号 if条件
info break
查看断点信息,delete
可以删除断点
- run或者r启动程序调试
- next或者n是单步调试
#include <iostream> using namespace std; int sum(int a, int b) { return a + b; } int main(){ int a = 100, b = 200; cout << sum(a,b) << endl; return 0; }
- p 变量名或者display 变量名可以查看变量值
step
命令或其简写s
来跳入函数内部调试;如果要从函数跳出则执行`finish
静态库
😄库的定义:
库是计算机的二进制文件,是一种代码仓库,可以给使用者提供一些
变量、函数或类
- 库是写好的现有的,成熟的,
可以复用
的代码。- 库没有main函数,库不能单独运行
😄在项目中使用库一般有两个目的:
- 一个是为了使程序更加简洁不需要在项目中维护太多的源文件
- 另一方面是为了源代码保密
汇编后得到的主程序的.o文件和库文件通过链接得到最后的可执行程序
静态库生成过程:
【静态库】:
在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中
一个静态库可以简单看成是一组目标文件(.o文件)的集合
静态库的制作
要使用库一定是由三个部分组成:库文件、头文件、文档说明
-
一般这个库本身就是函数的定义
-
头文件就是函数声明
例子:
- include下面的head.h文件,函数声明
int add(int, int); int sub(int, int);
- src下面的文件,具体函数的定义
int add(int a,int b){ return a + b; }
int sub(int a,int b){ return a - b; }
- main.cpp可执行程序
#include "head.h" #include <iostream> using namespace std; int main(){ cout << add(10, 20) << endl; cout << sub(30, 40) << endl; }
首先将src的cpp文件,汇编成.o文件,然后打包成静态库
将生成的静态库文件移动到:lib目录下
编译主程序需要指定头文件路径,使用的函数定义的库路径,还有具体的库文件名
当然也可以写在一起
g++ main.cpp -Iinclude -Llib -lMylib -o app
但是一定不能把main.cpp指定在自己定义的库文件之后
g++ -I include/ main.cpp -o app -L lib/ -lMylib
----对的
g++ -Iinclude -Llib main.cpp -lMylib -o app
----对的原因是因为main.cpp里面用到了静态库里的函数,所以静态库先指定
静态库特点总结:
- 静态库对函数库的链接是放在编译时期完成的。
- 程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
动态库
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时候才被载入。
动态库直接使用
编译器
即可创建动态库。静态库需要打包工具(ar)
动态链接库是程序运行时加载的库,当动态链接库正确部署之后,运行的多个程序可以使用同一个加载到内存
中的动态库,因此在Linux中动态链接库也可称之为共享库。
动态库的制作
使用gcc命令并且添加-FPIC(-fpic) 以及-shared 参数就可以生成动态库
使用上面的程序:
使用
把生成的动态库.so移动到lib,然后想静态库那样,编译main.cpp
-I 头文件文件夹路径 -L 库文件文件夹路径 -l 自定义库名
g++ main.cpp -I include/ -L lib/ -lmylib -o app
但是运行时候会出错
虽然已经告诉了编译器库文件和头文件的路径所在位置,但是当编译器编译好后,就与编译器无关了;
执行可执行程序是由加载器来完成的。所以需要在运行告诉系统
库文件在哪里
。
动态库原理
😄静态库工作原理:
在程序编译的最后一个阶段也就是链接阶段,提供的静态库会被打包到可执行程序中
当可执行程序被执行,静态库中的代码也会一并被加载到内存中
因此不会出现静态库找不到无法被加载的问题。
😄动态库加载方式:
- 在程序编译的最后一个阶段也就是链接阶段的gcc命令中:
库路径只是检查了这个路径下的库文件是否存在,对应的动态库文件也没有被打包到可执行程序中,只是在可执行程序中记录了库的名字。
- 可执行程序被执行起来之后:
程序执行的时候会先检测需要的动态库是否可以被加载,加载不到就会提示上边的错误信息。当动态库中的函数在程序中被调用了, 这个时候动态库才加载到内存,如果不被调用就不加载。
在链接期,动态链接只
在可执行程序中
记录动态链接库中共享对象的映射信息。在程序执行时,动态链接库的全部内容全部被映射到该进程的虚拟地址空间
本质就是把链接过程推迟到运行时处理
解决动态库的加载失败
可以通过ldd命令检查动态库的依赖关系
ldd : list dynamic depenfencies
报错原因就是:
系统加载动态库代码,系统只知道动态库的名字,但是不知道动态库代码绝对路径。因为动态库编译过程不会把库代码链接到动态库.so中。要想知道绝对路径,需要系统的动态载入器获取绝对路径。
动态库在程序运行后才会被动态加载到内存中
对于 elf 格式的可执行程序,是由 ld-linux.so 来完成的。
- elf的字段是不可以认为改变的
- /usr/lib涉及了系统,不要使用
使用环境变量指定动态库路径
执行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/Desktop/demo/lib
可以使用:echo查看是否导入成功
echo $LD_LIBRARY_PATH
用ldd可执行程序,看库是否有路径
ldd app
本种方法只能在一个终端起作用,新终端运行还得继续使用命令添加环境变量
利用主目录下的.bashrc文件或/etc/profile
sudo gedit .bashrc
在文件最底下引入上面的内容
使用source命令激活
source .bashrc
去/ect/profile文件中加上环境变量
sudo gedit /etc/profile
激活环境后可以运行
source /etc/profile
source可以用.代替
使用ld.so.cache指定动态库路径
ld.so.cache是一个二进制代码,需要在ld.so.conf里面添加
sudo gedit /etc/ld.so.conf
这里用ldconfig来激活
sudo ldconfig
静态库和动态库对比
静态库优缺点
- 静态库优点:
- 静态库被打包到应用程序中
加载速度快
- 发布程序
无需提供静态库
,移植方便
- 静态库缺点:
- 相同的库文件数据可能在内存中被
加载多份
, 消耗系统资源,浪费内存库 - 文件
更新需要重新编译
项目文件, 生成新的可执行程序, 浪费时间
动态库优缺点
- 动态库优点:
-
可实现不同进程间的资源共享
动态库被加载到内存后,所有进程可以访问该动态库的函数
-
动态库升级简单, 只需要替换库文件, 无需重新编译应用程序
动态库只有库名称打包到可执行程序,不会打包代码到可执行程序
-
程序员可以控制何时加载动态库, 不调用库函数动态库不会被加载
因为是运行时才会链接
- 动态库缺点
- 加载速度比静态库慢, 以现在计算机的性能可以忽略
- 发布程序需要提供依赖的动态库
程序使用动态库函数,才会把动态库动态加载到内存,而且加载一次后,其他进程可以访问