Linux基础开发工具(gcc/g++,yum,vim,make/makefile)
目录
软件包管理器——yum
Linux下,软件的安装
yum与软件包的关系
yum命令的运用
1.查看软件包
2.安装/删除软件包
编辑器——vim
vim的基本概念
vim的基本操作
命令模式命令
移动光标
删除文字
撤销上一次操作
跳至指定的行
底行模式命令
编译器——gcc/g++
预处理
编译
汇编
链接
库
动态链接与静态链接
总结
自动化构建——make/makefile
基本概念
实例
细节
语法扩展
Linux第⼀个系统程序——进度条
预备知识
练手——倒计时小程序
进度条简约版
进度条进阶版
调试器 - gdb/cgdb
预备知识点
常见命令
watch
条件断点
新建条件断点
在新增断点上设置条件
软件包管理器——yum
CentOS版本用yum,Ubuntu版本用apt。
Linux下,软件的安装
安装方法有三:
1.源码安装。下载程序的源代码,并进行编译,得到可执行程序。
2.rpm安装方式。使用 rpm
命令来安装 .rpm
格式的软件包。(不会自动解决依赖关系)
3.yum工具级别安装。(自动解决依赖关系)
注:这里的依赖关系是指一个软件可能需要依赖多个库才能实现。
yum与软件包的关系
yum命令的运用
1.查看软件包
[slm@localhost d1]$ yum list | grep lrzsz
lrzsz.x86_64 0.12.20-36.el7 @anaconda
注:
1,x86_64后缀表示64位系统的安装包、
2.el7表示的是操作系统发行版的版本。el7表示的是centos7
3.base表示的是“软件源的名称。
2.安装/删除软件包
sudo yum install -y 要安装的软件
sudo yum remove -y 要卸载的软件
注;安装要以超级用户的身份进行,拷贝下来的软件会在指定的系统目录中。
编辑器——vim
vim的基本概念
vim有很多中模式但我们掌握三种即可。三种模式分别是:命令模式,插入模式,底行模式。
用一张图来清晰解决三种模式的切换命令:
三种模式的特点:
命令模式:用命令对文件内容进行删除或者控制光标的移动等。
插入模式:可以用键盘的输入对文件内容进行修改。
底行模式:文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。
vim的基本操作
先进入文件 tmp.c
[slm@localhost d1]$vim tmp.c
命令模式命令
移动光标
按「G」:移动到文章的最后
按「 $ 」:移动到光标所在行的“行尾”
按「^」:移动到光标所在行的“行首”
按「w」:光标跳到下个字的开头
按「e」:光标跳到下个字的字尾
按「b」:光标回到上个字的开头
按「#l」:光标移到该行的第#个位置,如:5l,56l
按[gg]:进入到文本开始
按[shift+g]:进入文本末端
按「ctrl」+「b」:屏幕往“后”移动⼀页
按「ctrl」+「f」:屏幕往“前”移动⼀页
按「ctrl」+「u」:屏幕往“后”移动半页
按「ctrl」+「d」:屏幕往“前”移动半页
删除文字
「yw」:将光标所在之处到字尾的字符复制到缓冲区中。
「#yw」:复制#个字到缓冲区
「yy」:复制光标所在行到缓冲区。
「#yy」:例如,「6yy」表示拷贝从光标所在的该行“往下数”6行⽂字。
「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须
与“p”配合才能完成复制与粘贴功能。
撤销上一次操作
「u」:如果您误执行⼀个命令,可以马上按下「u」,回到上⼀个操作。按多次“u”可以执行
多次回复。
「ctrl + r」: 撤销的恢复
跳至指定的行
「ctrl」+「g」列出光标所在行的行号。
「#G」:例如,「15G」,表示移动光标至文章的第15行行首。
底行模式命令
列出行号
• 「set nu」: 输⼊「set nu」后,会在⽂件中的每⼀行前⾯列出行号。
• 跳到⽂件中的某⼀行
• 「#」:「#」号表示⼀个数字,在冒号后输⼊⼀个数字,再按回⻋键就会跳到该行了,如输⼊数字
15,再回⻋,就会跳到⽂章的第15行。
查找字符
• 「/关键字」: 先按「/」键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以
⼀直按「n」会往后寻找到您要的关键字为⽌。
• 「?关键字」:先按「?」键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可
以⼀直按「n」会往前寻找到您要的关键字为⽌。
保存⽂件
• 「w」: 在冒号输⼊字⺟「w」就可以将⽂件保存起来
离开vim
• 「q」:按「q」就是退出,如果⽆法离开vim,可以在「q」后跟⼀个「!」强制离开vim。
• 「wq」:⼀般建议离开时,搭配「w」⼀起使⽤,这样在退出的时候还可以保存文件。• 「q!」:不存盘强制退出vim
其他指令:
!man+函数:查询函数
!command:不离开vim,进入到命令行输入页
vs+文件名:一个窗口产生多个文件,如果文件不存在就新建
ctrl+ww:两个窗口之间关标切换
在命令行模式下批量化注释:
1.ctrl+v:进入可视化模式
2.按hjkl键选择要注释的行
3.shift+i=I//进入插入模式
4.按//
5.按两下Esc键
去注释:
1.ctrl+v:进入可视化模式
2.按hjkl键选择要删除的部分
3.按d键删除
编译器——gcc/g++
预处理
作用:进行宏替换/去注释/条件编译/头文件展开等
gcc -E hello.c -o hello.i
//选项”-E“作用是让gcc在预处理结束后停止编译过程
//选项”-o“是指目标文件,".i"文件是预处理的C原始程序
注:
1、头文件展开就是直接把头文件中相关的内容拷贝到源文件中,所以预处理后就不需要头文件了
2、条件编译(#if——#else——#endif),本质是对代码进行裁剪。比如我们下载软件的时候有时候会遇到社区版/专业版,在社区版中我们没有专业版软件的一些功能,两个版本的代码是差不多的,只是两个版本都新增了一些不同的代码让这两个版本之间产生差别。
3.gcc可以对代码进行增、删、改
编译
作用:检查代码的规范性。是否有语法错误等,检查无误后生成汇编语言。
gcc -S code.i -o code.s
//选项”S“的作用是让gcc在编译结束后停止汇编的过程
汇编
作用:生成机器可识别代码
gcc -c code.s -o code.o
//选项”c“的作用是让gcc在汇编结束后停止链接的过程
//不能直接执行,因为.o文件还没有定位到库文件中函数的地址,即还没有把正确的地址补在”地址还不确定的“机器码上。
链接
作用:生成可执行文件或库文件
gcc hello.o –o hello
//从.c文件直接形成可执行文件
gcc hello.c -o hello //形成hello可执行文件
gcc -o hello hello.c //形成hello可执行文件
gcc hello.c //形成a.out可执行文件
//牢记小秘诀:-o后面都是执行文件。
在上面的链接的过程中我们发现想要链接成功系统里就必须安装好库和头文件,不然找不到函数的地址,所以为什么要有库呢?
库
库的分类
常见的库有两种:动态库和静态库
动态库:在Linux下,后缀为”.so“;在windows下,后缀为”.dll“
静态库:在Linux下,后缀为”.a“;在windows下,后缀为”.lib”
库的命名
在Linux下,静态库:"libXXX.a",其中XXX是静态库的名字;动态库:“libXXX.so”,其中XXX是动态库的名字。
两种库的区别
静态库:会在磁盘进行链接时把库文件的代码全部加入到可执行文件中(实际上就是拷贝),所以生成的文件比较大,但是运行时不需要库文件了
动态库:在编译链接时并没有把库文件的代码加入到可执行文件中,而是在运行可执行文件时链接需要的库文件加载到可执行文件中,这样可以节省系统的开销。
两种库的优缺点
动态库 | 静态库 | |
优点 | 节省资源 | 不依赖任何库,可以独立运行 |
缺点 | 一旦丢失,所有程序无法直接运行 | 体积大,占据资源多,加载速度受影响 |
静态库和动态库的生成
gcc test.c -o test//形成动态链接
gcc test.c -o test_static -static//形成静态链接,比动态链接占用的空间大
ldd命令是查询文件依赖的库文件。
注:gcc默认生成的二进制程序,是动态链接的,可以用file命令检验。
动态链接与静态链接
动态链接就是把程序按照模块拆分成各个相对独立部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序。每个程序里面的库函数都是共用一个库的。
静态链接就是把代码需要的函数直接拷贝到可执行文件中,每个程序里面的库函数都要拷贝一份库函数代码,会产生很多副本。
总结
1.预处理(去注释/展开头文件/进行宏替换/条件编译等)
2.编译(检查代码规范性,检查语法错误,无误后生成汇编语言)
3.汇编(生成机器可识别二进制代码)
4.链接(生成可执行文件)
5.形成不同阶段文件的简易记法:Esc键变成ESc
有时候我们老是代码写完了,但是运行的时候会报错,但是修改完代码后,又要开始重新形成目标文件,万一我们的错误改了又错呢,错很多遍的话这样就会很麻烦。如果有个文件一直保存这条命令的话那我每次运行的时候就不需要自己又输一遍了。
自动化构建——make/makefile
基本概念
作用:一旦写好makefile,只需要一个make命令就可以让整个工程完全自动编译,极大提高了软件开发的效率。
make是解释makefile文件中指令的命令,所以make是一条命令,makefile是一个文件。
实例
test_9_10:test_9_10.cgcc -o test_9_10 test_9_10.c
.PHONY:clean
clean:rm -f test_9_10
规范:
细节
[root@localhost d1]# ll
总用量 904
drwxr-xr-x. 3 root root 16 3月 13 15:32 d2
-rw-r--r--. 1 root root 89 9月 10 15:30 makefile
-rwxr-xr-x. 1 root root 8664 9月 10 11:13 test
-rw-r--r--. 1 root root 204 9月 10 15:27 test_9_10.c
-rw-r--r--. 1 root root 553 9月 10 14:19 test.c
-rwxr-xr-x. 1 root root 900440 9月 10 11:13 test_static
[root@localhost d1]# make
gcc -o test_9_10 test_9_10.c
[root@localhost d1]# ll
总用量 916
drwxr-xr-x. 3 root root 16 3月 13 15:32 d2
-rw-r--r--. 1 root root 89 9月 10 15:30 makefile
-rwxr-xr-x. 1 root root 8664 9月 10 11:13 test
-rwxr-xr-x. 1 root root 8416 9月 10 15:31 test_9_10
-rw-r--r--. 1 root root 204 9月 10 15:27 test_9_10.c
-rw-r--r--. 1 root root 553 9月 10 14:19 test.c
-rwxr-xr-x. 1 root root 900440 9月 10 11:13 test_static
[root@localhost d1]# make
make: “test_9_10”是最新的。
[root@localhost d1]# make clean
rm -f test_9_10
[root@localhost d1]# make clean
rm -f test_9_10
[root@localhost d1]# make clean
rm -f test_9_10
在上面的代码中我们发现
问题1:make命令后只有一条执行指令,clean怎么不执行?
问题2:make命令执行后后面在执行就会显示文件是最新的,不会执行,但是clean却能一直执行
问题一:make、makefile默认只形成一个目标,就是从上到下遇到的第一个目标。但是如果第一条指令中依赖文件列表在该目录下没有,那么make就会往下面找,就会执行多条指令。
//makefile文件
test_9_10:test_9_10.ogcc -o test_9_10 test_9_10.o
test_9_10.o:test_9_10.sgcc -c test_9_10.s -o test_9_10.o
test_9_10.s:test_9_10.igcc -S test_9_10.i -o test_9_10.s
test_9_10.i:test_9_10.cgcc -E test_9_10.c -o test_9_10.i
.PHONY:clean
clean:rm -f test_9_10 *.i *.s *.o
[root@localhost d1]# ll
总用量 904
drwxr-xr-x. 3 root root 16 3月 13 15:32 d2
-rw-r--r--. 1 root root 278 9月 10 16:03 makefile
-rwxr-xr-x. 1 root root 8664 9月 10 11:13 test
-rw-r--r--. 1 root root 204 9月 10 15:27 test_9_10.c
-rw-r--r--. 1 root root 553 9月 10 14:19 test.c
-rwxr-xr-x. 1 root root 900440 9月 10 11:13 test_static
[root@localhost d1]# make
gcc -E test_9_10.c -o test_9_10.i
gcc -S test_9_10.i -o test_9_10.s
gcc -c test_9_10.s -o test_9_10.o
gcc -o test_9_10 test_9_10.o
[root@localhost d1]# ll
总用量 944
drwxr-xr-x. 3 root root 16 3月 13 15:32 d2
-rw-r--r--. 1 root root 278 9月 10 16:03 makefile
-rwxr-xr-x. 1 root root 8664 9月 10 11:13 test
-rwxr-xr-x. 1 root root 8416 9月 10 16:03 test_9_10
-rw-r--r--. 1 root root 204 9月 10 15:27 test_9_10.c
-rw-r--r--. 1 root root 16921 9月 10 16:03 test_9_10.i
-rw-r--r--. 1 root root 1616 9月 10 16:03 test_9_10.o
-rw-r--r--. 1 root root 576 9月 10 16:03 test_9_10.s
-rw-r--r--. 1 root root 553 9月 10 14:19 test.c
-rwxr-xr-x. 1 root root 900440 9月 10 11:13 test_static
问题2:首先我们先了解“.PHONY”表示被修饰的目标是一个伪目标,伪目标总是要执行的。总是要执行就表示它不需要对比源文件和exe的修改时间,只要make它的指令就会可以一直执行。
[root@localhost d1]#stat test_9_10.c文件:"test_9_10.c"大小:204 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:33600218 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root)
环境:unconfined_u:object_r:home_root_t:s0
最近访问:2025-09-10 15:31:25.887293504 +0800
最近更改:2025-09-10 15:27:21.934213096 +0800
最近改动:2025-09-10 15:27:21.936213031 +0800
创建时间:-
[root@localhost d1]# vim test_9_10.c
[root@localhost d1]# stat test_9_10.c文件:"test_9_10.c"大小:216 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:33600234 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root)
环境:unconfined_u:object_r:home_root_t:s0
最近访问:2025-09-10 19:39:14.639976185 +0800
最近更改:2025-09-10 19:39:14.639976185 +0800
最近改动:2025-09-10 19:39:14.642975744 +0800
创建时间:-
//⽂件 = 内容 + 属性
//Modify: 内容变更,时间更新(最近改动)
//Change:属性变更,时间更新(最近更改)
//Access:常指的是⽂件最近⼀次被访问的时间。在Linux的早期版本中,每当⽂件被访问时,其
//atime都会更新。但这种机制会导致⼤量的IO操作。具体更新原则,不做过多解释。
make执行第一条不是伪目标的指令时,会先比较文件的源文件是否在可执行文件的前后时间,来决定是否要重新编译。如果源文件的修改时间比可执行文件晚,那么就不需要重新编译,否则,需要。
语法扩展
BIN=proc.exe | 定义变量(类似于宏替换) |
SRC=$(shell ls *.c) | 采⽤shell命令⾏⽅式,获取当前所有.c⽂件名 |
SRC=$(wildcard *.c) | 使⽤ wildcard 函数,获取当前所有.c⽂件名 |
OBJ=$(SRC:.c=.o) | 将SRC的所有同名.c 替换 成为.o 形成⽬标⽂件列表 |
LFLAGS=-o | 链接选项 |
FLAGS=-c | 编译选项 |
RM=rm -f | 引⼊命令 |
$(BIN):$(OBJ) @$(CC) $(LFLAGS) $@ $^ | $@:代表⽬标⽂件名。 $^: 代表依赖⽂件列表 |
@$(CC) $(FLAGS) $< | %<: 对展开的依赖.c⽂件,⼀个⼀个的交给gcc。 |
@echo "compling ... $< to $@" | @:不回显命令,如果不加 @,终端会显示:
然后再显示:
加了
|
clean: $(RM) $(OBJ) $(BIN) # | $(RM): 替换,⽤变量内容替换它 |
%.o:%.c | %.c 展开当前⽬录下所有的.c。 %.o: 同时展开同 名.o |
实例:
1 BIN=test_9_102 SRC=$(wildcard *.c)3 OBJ=$(SRC:.c=.o)4 LFLAGS=-o5 FLAGS=-c6 CC=gcc7 RM= rm -f8 $(BIN):$(OBJ)9 @$(CC) $(LFLAGS) $@ $^10 %.o:%.c11 @$(CC) $(FLAGS) $< 12 @echo "compling ...$< to $@"13 .PHONY:clean14 clean:15 $(RM) $(OBJ) $(BIN) //相当于rm -f *.o test_9_10
执行make指令:
[root@localhost d1]# make
compling ...test_9_10.c to test_9_10.o
[root@localhost d1]# ll
总用量 904
drwxr-xr-x. 3 root root 16 3月 13 15:32 d2
-rw-r--r--. 1 root root 228 9月 10 20:42 makefile
-rwxr-xr-x. 1 root root 8416 9月 10 20:35 test_9_10
-rw-r--r--. 1 root root 204 9月 10 20:35 test_9_10.c
-rw-r--r--. 1 root root 1616 9月 10 20:35 test_9_10.o
-rwxr-xr-x. 1 root root 900440 9月 10 11:13 test_static
[root@localhost d1]# make clean
rm -f test_9_10.o test_9_10
[root@localhost d1]# ll
总用量 888
drwxr-xr-x. 3 root root 16 3月 13 15:32 d2
-rw-r--r--. 1 root root 228 9月 10 20:42 makefile
-rw-r--r--. 1 root root 204 9月 10 20:35 test_9_10.c
-rwxr-xr-x. 1 root root 900440 9月 10 11:13 test_static
Linux第⼀个系统程序——进度条
预备知识
回车(\r)/换行(\n):
\r:把光标移到行首(不换行)
\n:把光标移到下一行
行缓冲区:
函数:fflush
#include<stdio.h> int fflush(FILE*stream)//把用户缓冲区的内容刷新到内核缓冲区中,并写在文件stream中
比较下面三种结果有何不同:
1 #include<stdio.h>2 int main()3 {4 printf("hello world\n");5 sleep(3);6 return 0;7 }
现象:立马打印“hello world”,然后光标在下一行闪烁三秒,然后打印命令条
1 #include<stdio.h>2 int main()3 {4 printf("hello world");5 sleep(3);6 return 0;7 }
现象:等待三秒后才打印“hello world”,然后在同一行上打印命令条
1 #include<stdio.h>2 int main()3 {4 printf("hello world");5 fflush(stdout);6 sleep(3);7 return 0;8 }
现象:立马打印“hello world”,等待三秒后,然后在同一行上打印命令条
所以从上面的现象可以得出,
1.回车键和fflush函数可以进行行缓冲,把用户缓冲区的内容刷新到内核缓冲区并刷新到输出设备上。
2.程序结束也会自动刷新缓冲区。
练手——倒计时小程序
1 #include<stdio.h>2 int main()3 {4 int i=20;5 while(i--)6 {7 printf("%-2d\r",i);//保留两位数字8 fflush(stdout);9 sleep(1);10 11 }12 printf("\n");13 return 0;14 }
进度条简约版
//每秒加载1%1 #include<stdio.h>2 #include<string.h>3 int main()4 {5 char str[101]={0};6 int i=0;7 for(i=0;i<=100;i++)8 {9 printf("[%-100s] %d%%\r",str,i);10 str[i]='#';11 fflush(stdout);12 sleep(1);13 14 }15 printf("\n");16 17 return 0;18 }
我们常见的进度条都是会根据我们的网速来确定加载了多少,所以我们来实现一个进阶版
进度条进阶版
[root@localhost d1]# ./process
[####################################################################################################] [-] [100%]
download 1025.00MB Done
main.c
1 #include"process.h"2 double speed=1.0;3 double size=1024.0;4 void download()5 {6 7 double current=0;8 while(current<=size)9 {10 //打印进度条11 PrintfProcess(current,size);12 current+=speed;//注释掉,可以观察到进度一直在加载13 usleep(20000);14 }15 printf("\ndownload %.2lfMB Done\n",current);//current类型写错,输出0.0016 }17 int main()18 {19 download();20 return 0;21 }
process.c
1 #include"process.h"2 void PrintfProcess(double current,double size)3 {4 char str[101];5 memset(str,0,sizeof(str));6 const char*lable="-\\|/";//“\\”转义字符用来表示进度的加载效率7 int len=strlen(lable);8 int i=0;9 int num=(int)(current/size*100);10 for(i=0;i<num;i++)11 {12 str[i]='#';13 }14 static int cnt;15 cnt%=len;16 printf("[%-100s] [%c] [%.lf%%]\r",str,lable[cnt],current/size*100);17 cnt++;//即使卡顿了也一直在加载,检测网络在不在线18 fflush(stdout);19 }
lable字符串的作用:
process.h
1 #pragma once2 #include<stdio.h>3 #include<unistd.h>4 #include<string.h>5 void PrintfProcess(double current,double size);
makefile
1 SRC=$(wildcard *.c)2 OBJ=$(SRC:.c=.o)3 BIN=process4 $(BIN):$(OBJ)5 gcc -o $@ $^6 %.o:%.c7 gcc -c $<8 .PHONY:clean9 clean:10 rm -f $(OBJ) $(BIN)
调试器 - gdb/cgdb
样例代码:
// mycmd.c
#include <stdio.h>
int Sum(int s, int e)
{int result = 0;for(int i = s; i <= e; i++){result += i;}return result;
}
int main()
{int start = 1;int end = 100;printf("I will begin\n");int n = Sum(start, end);printf("running done, result is: [%d-%d]=%d\n", start, end, n);return 0;
}
预备知识点
1.程序有两种发布方式,debug模式和release模式。Linux gcc/g++出来的二进制程序,默认时release模式。
2.debug模式才允许被调试,所以要使用gdb调试,必须在源代码生成二进制程序的时候,加上-g选项,如果没有添加,就不能进行调试。
[root@localhost d1]# gcc -o mycmd mycmd.c//默认模式,不支持调试
[root@localhost d1]# gdb mycmd
(gdb) l 1
No symbol table is loaded. Use the "file" command.
(gdb) quit
[root@localhost d1]# gcc -o mycmd mycmd.c -g//debug模式,支持调试
[root@localhost d1]# gdb mycmd
(gdb) l 1
5 int i;
常见命令
进入调试:gdb+可执行文件
退出调试:ctrl+d或q
命令 | 作⽤ | 样例 |
list/l | 显示源代码,从源文件中光标那行开始 | list/l 10 |
list/l 函数名 | 列出指定函数的源代码 | list/l mian |
list/l 文件名:行号 | 列出指定文件的源代码,如果只要指定的哪一行,那么就先设置 set listsize 1,就可以指定看哪一行,否则都是从第一行开始展示 | list/l mycmd.c:1 |
r/run | 从程序开始连续执行 | run |
n/next | 单步执行,不进入函数内部,相当于逐过程F10 | next |
s/step | 单步执行,进入函数内部,相当于逐语句F11 | |
break/b | 在指定行号设置断点 | b 10 break mycmd.c 10 |
break/b 函数名 | 在函数开头设置断点 | break main |
info break/b | 查看所有断点信息 | info break |
finish | 执行到当前函数的返回,然后停止 | |
print/p 表达式 | 打印表达式的值 | print end+start |
p 变量 | 打印指定变量的值 | p x |
set var 变量=值 | 修改变量的值 | set var i=10 |
continue/c | 从当前位置开始连续执行程序 | continue |
delete/d breakpoints | 删除所有断点 | |
delete/d breakpoints n | 删除序号为n的断点 | delete/d breakpoints 1 |
disable breakpoints | 禁用所有断点 | |
enable breakpoints | 启用所有断点 | |
display 变量名 | 跟踪显示指定变量的值(每次停止时) | display x |
undisplay 编号 | 取消对指定编号的变量的跟踪显示 | undisplay 1 |
until x行号 | 执行到指定行号 | until 10 |
backtrace/bt | 查看当前执行栈的各级函数调用及参数 | |
info/i locals | 查看当前栈帧的局部变量值 | |
quit/q | 退出gdb调试器 |
注:n、s、finish、c、display/undisplay、until都必须在执行期间才可以使用。
watch
执行时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运行期间的值发生变化,GDB 会暂停程序的执行,并通知使用者。
(gdb) l main
8 result += i;
9 }
10 return result;
11 }
12 int main()
13 {
14 int start = 1;
15 int end = 100;
16 printf("I will begin\n");
17 int n = Sum(start, end);
(gdb) b 17
Breakpoint 1 at 0x4005cd: file mycmd.c, line 17.
(gdb) r
Starting program: /home/d1/mycmd
I will beginBreakpoint 1, main () at mycmd.c:17
17 int n = Sum(start, end);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.3.x86_64
(gdb) s
Sum (s=1, e=100) at mycmd.c:4
4 int result = 0;
(gdb) n
6 for(i = s; i <= e; i++)
(gdb) watch result
Hardware watchpoint 2: result
(gdb) c
Continuing.
Hardware watchpoint 2: resultOld value = 0
New value = 1
Sum (s=1, e=100) at mycmd.c:6
6 for(i = s; i <= e; i++)
(gdb) c
Continuing.
Hardware watchpoint 2: resultOld value = 1
New value = 3
Sum (s=1, e=100) at mycmd.c:6
6 for(i = s; i <= e; i++)
注:如果你有一些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如
果变化了,就会通知。
条件断点
实际上就是在设置断点的时候给断点加个条件,当运行起来后finish就会停在设置条件的那个位置。比如 b 8 if i==30,那么程序就会在第八行且i=30的时候停止执行。可以验证中间部分是否结果错误。
新建条件断点
(gdb) l Sum
1 #include <stdio.h>
2 int Sum(int s, int e)
3 {
4 int result = 0;
5 int i;
6 for(i = s; i <= e; i++)
7 {
8 result += i;
9 }
10 return result;
(gdb) l main
8 result += i;
9 }
10 return result;
11 }
12 int main()
13 {
14 int start = 1;
15 int end = 100;
16 printf("I will begin\n");
17 int n = Sum(start, end);
(gdb) b 17
Breakpoint 1 at 0x4005cd: file mycmd.c, line 17.
(gdb) r
Starting program: /home/d1/mycmd
I will beginBreakpoint 1, main () at mycmd.c:17
17 int n = Sum(start, end);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.3.x86_64
(gdb) s
Sum (s=1, e=100) at mycmd.c:4
4 int result = 0;
(gdb) s
6 for(i = s; i <= e; i++)
(gdb) n
8 result += i;
(gdb) n
6 for(i = s; i <= e; i++)
(gdb) n
8 result += i;
(gdb) b 8 if i==30
Breakpoint 2 at 0x400596: file mycmd.c, line 8.
(gdb) display i
1: i = 2
(gdb) finish
Run till exit from #0 Sum (s=1, e=100) at mycmd.c:8Breakpoint 2, Sum (s=1, e=100) at mycmd.c:8
8 result += i;
1: i = 30
(gdb) p result//可以查看result的值
$1 = 435
在新增断点上设置条件
(gdb) b 17
Breakpoint 1 at 0x4005cd: file mycmd.c, line 17.
(gdb) r
Starting program: /home/d1/mycmd
I will beginBreakpoint 1, main () at mycmd.c:17
17 int n = Sum(start, end);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.3.x86_64
(gdb) s
Sum (s=1, e=100) at mycmd.c:4
4 int result = 0;
(gdb) s
6 for(i = s; i <= e; i++)
(gdb) n
8 result += i;
(gdb) n
6 for(i = s; i <= e; i++)
(gdb) n
8 result += i;
(gdb) n
6 for(i = s; i <= e; i++)
(gdb) n
8 result += i;
(gdb) b
Breakpoint 2 at 0x400596: file mycmd.c, line 8.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004005cd in main at mycmd.c:17
breakpoint already hit 1 time
2 breakpoint keep y 0x0000000000400596 in Sum at mycmd.c:8
(gdb) condition 2 i==30 //不需要if
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004005cd in main at mycmd.c:17
breakpoint already hit 1 time
2 breakpoint keep y 0x0000000000400596 in Sum at mycmd.c:8
stop only if i==30
breakpoint already hit 2 times
(gdb) n
6 for(i = s; i <= e; i++)
(gdb) n
8 result += i;
(gdb) c
Continuing.Breakpoint 2, Sum (s=1, e=100) at mycmd.c:8
8 result += i;
(gdb) p i
$2 = 30
(gdb) p result
$3 = 435
注:Ctrl+X松开后按a就可以进入GDB TUI(Text User Interface)模式 下的分屏操作,按Esc可以切换到源代码区域,可以按上下键实现翻页;按i键可以切换回gdb模式。