【项目一】GCC(gcc,g++)、静态库、动态库、MakeFile、GDB调试

准备回过头复习的时候,看第四章开头的20分钟,对前四章的重要知识做了梳理概括。

1.2 GCC(1)gcc

在这里插入图片描述

(1)常用命令

1、ctrl+l 清空屏幕
2、使用gcc编译
gcc xx.c -o app
解释:gcc + 文件名 + -o(用于指定编译后的文件名) + app(文件名)
3、生成可执行文件
./app
解释:运行刚刚编译出来的文件
如果gcc xx.c -o app不加-o,只有gcc xx.c
那么会生成一个a.out的可执行程序

(2) C程序编译过程

在这里插入图片描述

高级语言 编译 成汇编语言
汇编语言 汇编 成机器语言
机器语言 运行

(3)GCC工作流程

在这里插入图片描述

预处理:
1、展开头文件,将头文件中的内容复制进源代码
2、删除注释
3、对定义的宏做宏替换
指令: gcc test.c -E -o test.i

编译器:
将源代码编译成汇编代码(.s)
gcc test.i -S -o test.s
直接执行gcc test.c -S也会生成.s文件(就包含了预处理这一步)

汇编器:
将汇编代码汇编成目标代码(.O)
gcc test.s -c -o test.o

链接:
-c 生成目标代码 test.o,test.o 不是一个可执行程序。如果下一步需要继续链接成可执行程序,需要输入指令 gcc test.o -o test.out(这里直接将 .o 目标文件链接成可执行程序 test.out,可以有多个 .o 文件,这里只有一个

所以整个直接 gcc test.c 包含了预处理,编译,汇编,链接成可执行文件4步。

1.3 GCC(2)g++

别管那么多,c就用gcc,cpp就应g++

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
-D使用示例:


#include <stdio.h>
#define PI 3.14

int main(){
    int a = 10;
#ifdef DEBUG
    printf("我不会爬树\n");
#endif // DEBUG
    for(int i = 0; i < 3; ++i){
        printf("hello,GCC\n");
    }
    return 0;
}   

在这里插入图片描述
-Wall使用示例:
在这里插入图片描述

在这里插入图片描述

1.3静态库的制作

在这里插入图片描述静态库实际上是将.c文件们打包。这些.c文件定义的函数,都在.h文件中申明。这样只需要把静态库和头文件给用户,用户就可以知道那些函数可以调用,但不清楚函数的具体实现。

-c使用示例:(生成.o文件)
在这里插入图片描述制作静态库之前,首先需要把.c文件用gcc命令和 -c选项做成.o文件。 src目录下:gcc -c add.c div.c mult.c sub.c 是要报错的,因为找不到.h文件。如下所示。
在这里插入图片描述所以实际上还要加上-I(大写i)选项,后面跟上.h所在的文件夹路径,告诉编译器去这个路径下找头文件:
gcc -c add.c div.c mult.c sub.c -I …/include/
注:.c文件在-c选项的前面还是后面无所谓
gcc add.c div.c mult.c sub.c -c -I …/include/
也可以。

ar命令的使用:(将.o文件打包成静态库)
ar rcs libsuanshi.a add.o div.o sub.o mult.o
移动命令:mv + 文件名 + 目的文件夹路径

1.5静态库的使用

在这里插入图片描述

参数选项
1、-I(大写i) :查找头文件的路径 (后面跟目录)
使用: -I 路径
例如:gcc main.c -o app -I ./include/
在这里插入图片描述此时只找到了头文件,但没找到这些函数的定义,也就是静态库

2、-l(小写L):指定加载哪个库(后面跟的是库名字,不是库的文件名)
文件名:libcala.a
库名:calc

3、-L:指定到哪个文件夹下去找静态库(后面跟目录)

示例:
gcc main.c -o app -I ./include/ -l calc -L ./lib/
在这里插入图片描述

1.6动态库的制作

在这里插入图片描述

什么叫:与位置无关?
“与位置无关”(Position-Independent Code,简称PIC)的代码是一种可执行代码,它在内存中的位置不影响其执行。换句话说,它可以在任何地方执行,而不需要修改。
这是通过使所有代码引用都通过间接寻址实现的,如通过全局偏移表(Global Offset Table,简称GOT)。例如,不是直接引用数据或函数的绝对地址,而是从GOT中查找这些地址。
这种方法的主要好处之一是,可以创建可以在多个程序之间共享的库。这就是所谓的动态链接库或共享库。因为每个程序可以将库加载到内存中的不同位置,而库代码的执行不受其在内存中的实际位置影响,因此可以共享。
如果你在用gcc编译代码时,可以使用-fPIC或-fpic选项来生成与位置无关的代码。这两个选项之间的区别在于,-fpic生成的代码使用的GOT较小,因此可能在大型程序中无法使用,而-fPIC则没有这个限制,但生成的代码可能会稍大一些。在大多数情况下,建议使用-fPIC选项。

1、制作动态库:
生成与位置无关的.o文件
gcc -c -fpic add.c div.c sub.c mult.c (如果头文件不在,记得指定头文件文件夹 -I …/include/ )
生成动态库
gcc -shared add.o sub.o mult.o div.o -o libcalc.so

2、使用动态库
在这里插入图片描述
与使用静态库一样,要指定头文件所在路径。动态库所在路径以及要使用的动态库名字(不是文件名字)
gcc main.c -o main -I ./include/ -l calc -L ./lib/

但这样使用会报错,运行main这个可执行文件的时候找不到动态库文件
在这里插入图片描述

1.7动态库加载失败的原因

原因:gcc在链接时动态库代码不会被打包进可执行文件中,只会放入一些相关信息。所以在程序运行以后,需要通过系统的动态载入器找到动态库的绝对路径(找的顺序如下图所示),然后把动态库加载到内存中。如果我们没给这个绝对路径,那就会找不到动态库。
在这里插入图片描述

ldd:list dynamic dependendcies 列出动态依赖库
在这里插入图片描述后面的一串数组是内存地址,此时可以看到 libcalc.so这个动态库没有被找到。

每个程序就是一个进程,linux会为每个进程分配虚拟地址空间(DT_RPATH段)

1.8解决动态库加载失败的原因(用add把动态库加载到内存中去)

1、配置环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wcx/linux/lesson06/library/lib(冒号后面是动态库的绝对路径)
查看是否配置成功
1)echo $LD_LIBRARY_PATH
回车以后会显示绝对路径
在这里插入图片描述

2)ldd + 可执行文件
会列出该文件需要的动态库以及各个库的位置
在这里插入图片描述

弊端
在终端的环境变量里配置只是临时的,一旦开一个新终端就失效了

2、用户级别的配置
配置.bashrc文件,在home目录下(~)
vim打开
shift+G跳到最后一行
o 往下插入一行
输入配置路径:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wcx/linux/lesson06/library/lib
esc退出编辑模式,shift+:后,wq保存并退出
配置完成后还需要使文件生效 . .bashrc。第一个点相当于source,所以写成 source .bashrc也可以

这样配置好的动态库可以直接运行可执行文件,不必再指定头文件和库文件的位置。

3、系统级别的配置
需要管理员权限
sudo
vim /etc/profile
到最后一行
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wcx/linux/lesson06/library/lib
保存退出
配置完成后还需要使文件生效 . /etc/profile。第一个点相当于source,所以写成 source /etc/profile也可以

以上两种配置环境变量的方式打开新的终端可能还要source以下文件才能生效。同样,如果删除这条环境变量,需要开一个新终端才能生效。

4、配置 /etc/ld.so.cache文件
该文件是二进制文件,无法直接输入
所以要通过sudo vim /etc/ld.so.conf文件配置
进入文件后,将绝对路径复制进去就行/home/wcx/linux/lesson06/library/lib
更新:sudo ldconfig ( ldconfig第一个字母是小写L)

5、把文件放到 /lib/ 或者 /user/lib目录下
但是不建议,因为里面本身就包含了许多系统自带的库文件,如果自己写得库文件和系统重名,有可能会被替换,很危险。

1.9静态库与动态库对比

总得来说:库比较小就用静态库,比较大就用动态库
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

发布程序无需提供静态库,移植方便
打包成可执行程序以后直接把可执行程序给别人就可以,不用给静态库,因为静态库已经复制进去了。

消耗系统资源,浪费内存
多个程序使用同一个静态库的时候,每一个程序都会把静态库复制进去

更新、部署、发布麻烦
由于每个程序之间相互独立,如果静态库做修改,那么所有涉及到这个静态库的程序都要重新编译

在这里插入图片描述

1.10MakeFile

在这里插入图片描述在这里插入图片描述1、创建MakeFile文件示例
以该文件夹下文件为例:
在这里插入图片描述1)vim Makefile 创建新文件并进入
2)编写Makefile
在这里插入图片描述3)执行make命令
会自动找到当前目录下的那个Makefile文件

在这里插入图片描述
命令在执行之前,需要先检查规则中的依赖是否存在
如果存在,执行命令
如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的如果找到了,则执行该规则中的命令
在这里插入图片描述

比如这里第一条规则要找.o文件
不存咋,所以往下看其他规则,先把.c文件生成成.o文件
此外,由于MakeFile中其他规则都是为第一条规则服务的,如果与第一条规则无关,那就不会被执行。比如最后一行有个
b.o:b.c
gcc -c b.c -o b.o
跟第一行规则无关,就不会生成b.o文件

检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间
如果依赖的时间比目标的时间晚,需要重新生成目标
如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被执行

在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述

现在,在第一条规则中找依赖。发现4个.o文件都没有,于是会向下找规则来生成.o文件。四个.o文件其实用的规则是一样的,所以我们可以用模式匹配。首先找sub.o文件,发现下面有规则适用,于是调用。然后找add.o发现这个规则也适用,于是继续调用。

在这里插入图片描述

$(函数名 参数)
上图中,wildcard就是函数名 wildcard:未知数
示例中:.c ./sub/.c 是两个目录,意思是要获取当前目录下的所有.c文件,以及当前目录下的sub目录下,所有.c文件。多个目录之间用空格隔开

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

关于伪命令
clean:
rm &(objs) -f
这条规则其实不生成任何文件
但如果在当前目录下有了一个clean文件,在执行这条命令的时候就会比较依赖和目标的前后。由于没有依赖,目标就会是最新的,这个命令就会一直不执行。
所以要在前面加上 .PHONY,说明他后面跟的目标都被是伪目标。这样即使有clean文件也不影响这个命令的执行。
注:
在执行make的时候,由于clean和第一条规则无关,所以不会自动执行,要 make clean才会执行

1.13 GDB调试

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

1.13.1 GDB准备、启动、退出、查看代码

1、准备工作:使用-g选项生成带源码信息的可执行程序
在这里插入图片描述可以看到,test文件大于test1文件,说明-g选项添加一些源码信息到可执行文件中。
注:-h选项是在列出文件信息的时候,把文件大小转成更容易阅读的K,KB ,G等大小

2、启动调试
gdb+可执行程序
在这里插入图片描述
3、给程序设定参数
test.c文件中可以看到,main函数是有参数的
在这里插入图片描述
设置参数:set args +参数

显示参数:show args
在下一行就会有:程序开始调试时的参数列表是:10 20在这里插入图片描述4、退出gdb调试模式
直接写quit或者q就可以
在这里插入图片描述5、help
在调试模式下,键入help,help all是显示所有帮助信息,可以回车显示下一屏。
如果需要有针对显示 可以写 help set
那么就会显示所有set开头的命令,比如 set args

5、查看代码
vim test.c
set nu 显示行号
也可以在gdb里面查看
1)list就可以,一次显示10行,回车或者list显示下面10行
2)list + 行号:list 20 就会把20作为中间行,显示15-25行
3)list+函数名:list main 会把main函数开始的那一行作为中间行显示出来
4)有时候可执行文件不会只有一个.c文件生成
在这里插入图片描述
此时直接输入list或者l,默认显示main函数所在的文件
如果查看非当前文件代码:
list/1 文件名:行号:list bubble.cpp:1
list/1 文件名:函数名:list select.cpp:selectSort

6、设置显示的行数
show list/listaize
set list/lintaize 行数
在这里插入图片描述

1.13.2 GDB断点操作

在这里插入图片描述在这里插入图片描述

在设置断点的时候,和list一样,如果不指定文件,断点就打在main函数所在的文件里

删除断点

在这里插入图片描述使断点无效

在这里插入图片描述

设置条件断点

在这里插入图片描述

只有当i=3时,才会在16行停住。

1.13.3 GDB利用断点查看数值

在这里插入图片描述
1、运行GDB程序
start:程序停在第一行
run(遇到断点才停)
2、继续运行,到下一个断点停
c/continue
要是接下来没有断点,就会到程序结尾

在这里插入图片描述

在这里插入图片描述
打一些断点:

在这里插入图片描述

在这里插入图片描述

run 跑到第一个断点处
c 继续跑直到下一个断点。断点2是bubble文件的bublesort函数,所以会落到这个函数内的第一行,也就是for。list以下就能看到这个文件的第八行确实是函数内的第一行for
在这里插入图片描述

第三个断点在main的16行,是在一个for循环内
在这里插入图片描述
在这里插入图片描述

打印变量名
第一次打印i是2,continue(c),跑到下一个断点(因为在for循环内所以又到16行),此时再打印i是3.
在这里插入图片描述
打印变量类型
在这里插入图片描述

跳出循环
1、循环内不能有断点。
2、执行until,此时程序会一直执行,直到不再能够进入函数体
3、要连续两次until。并且循环内至少执行一次才能跳出循环
在这里插入图片描述
在这里插入图片描述
一开始 i = 2时,执行until,就会一直跑到 i = len。此时再执行step,就会到cout<<endl

next与step的区别
next遇到函数不会进入
在这里插入图片描述
step碰到函数会进入
在这里插入图片描述
注意:
如果想用finish跳出函数,函数体内不能有断点

自动变量操作
用test做测试
在这里插入图片描述
在这里插入图片描述
如果想查看a和b的值,每一次都print的话太麻烦,就可以设置的自动变量
display a
这样接下来每操作一次,都会自动输出a的值
在这里插入图片描述
还可以查看设置了哪些自动变量:info display
如果想取消自动变量:undisplay 编号 就行

设置变量值 set var
在这里插入图片描述
这里i走到3,如果想从i = 9开始走
就可以set var i= 9

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值