目录
前言
一、git中的常见命令:
git常见命令
gitignore介绍
二、gdb预备知识:编译与模式
2.1 gdb常见命令
2.2 断点之用途
2.3 程序的运行与暂停
2.4 堆栈跟踪
三、gdb中的三种调试技巧
3.1 变量监测(watch)
3.2 set var
3.3 条件断点
本文系统介绍了Git版本控制与GDB调试的核心知识。Git部分涵盖常用命令(git add、commit、push)、.gitignore文件作用及冲突解决策略;GDB部分重点讲解编译调试模式(-g选项)、断点设置、单步执行、堆栈跟踪及变量监控技巧。结合实例演示了watch监测变量、setvar动态修改变量值、条件断点等高级调试方法,助力高效定位代码问题。
前言
理解版本控制:也就是对历史版本进行备份
Git 是分布式版本管理工具:帮你保存代码的每一次快照,随时回退、对比、分支开发,不会因为改代码把旧版本搞丢。
git不仅具有版本控制功能,还具有网络同步功能(用来在多个仓库之间互相传输版本快照)。
git可以在本地使用,而gitee则可以建立一个本地仓库用于协同合作,那么首先我们需要在本地安装一个git,命令如下:
[root@VM-0-4-centos ~]# yum install -y git那么上面我们说了,在gitee中可以建立一个远端仓库,那么远端仓库怎么下载到本地呢,这里我们首先找到克隆
点击后选择这个栏目下的链接即可(这里为了保密我没有展示)
将链接复制后打开XShell,使用该命令即可实现远端仓库下载到本地
[root@VM-0-4-centos ~]# git clone https://gitee.com/-----//后面的详细信息被我省略了此外在使用cgdb时我们会有一个小问题,就是有时光标会消失,按什么键都没有反应,这一般时因为误触了滚轮,方向键等导致光标切换到代码区了,这时只需要按下 i 即可恢复,再按ESC又可切换
一、git中的常见命令:
git常见命令
git add:把修改信息添加到暂存区git commit -m "描述":把暂存区打包成本地版本快照(存在本地.git 仓库)双引号中为日志git push:把本地所有新提交的版本上传到远程仓库
对于上述命令我们需要注意的是,执行这三条命令如果想同步到远端仓库的话需要先进入git clone所生成的那个文件夹,否则会报错
[root@VM-0-4-centos kks-paradise]# ls -al total 24 drwxr-xr-x 3 root root 4096 Oct 17 10:27 . dr-xr-x---. 13 root root 4096 Oct 17 16:00 .. drwxr-xr-x 8 root root 4096 Oct 17 17:04 .git #这个就是我们的仓库,所有的版本记录全在这里 -rw-r--r-- 1 root root 270 Oct 17 10:27 .gitignore -rw-r--r-- 1 root root 955 Oct 17 10:27 README.en.md -rw-r--r-- 1 root root 1316 Oct 17 10:27 README.mdgitignore介绍
作用:告诉 Git:哪些文件 / 文件夹不要纳入版本管理,不要提交到仓库
可以在ignore中添加需要被忽略的后缀,它通过这样的方法过滤掉带有特定后缀的文件,让对应的临时文件不被管理。
那么在多人协作时如果出现了本地和远端历史不一样时(出现了冲突),先把远端最新的提交到本地合并,本地代码变成最新版本,再push上传,远端仓库的价值在于保持最新
命令:git pull=git fetch(下载远端快照) +git merge(合并到本地当前分支)
那么上述我们已经简单了解了怎么管理代码,接下来我们来了解怎么调试代码
二、gdb预备知识:编译与模式
linux下gcc/g++编译的程序默认是release模式,无法直接用gdb调试,要启用调试,必须再编译时添加 -g选项,生成debug模式的可执行文件。
Reading symbols from xxx...(no debugging symbols found)...done. # 括号中的内容表示无法调试,没有调试符号一般gcc和g++编译程序,默认是以release方式发布的,如果通过这个方式发布的程序,就无法调试,解决方法是添加 -g (调试)
mycmd:mycmd.c gcc -g mycmd.c -o mycmd2.1 gdb常见命令
首先准备一段调试用代码:
#include <stdio.h> int Sum(int s, int e) { int result = 0; int i=s; for (; 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; }gdb/cgdb 中的命令均可简写,只需打出首字母,比如d为delete,下面我们举出几个常见的gdb中的命令。(cgdb的区别是有一个可视化界面,需要自行安装,一般建议使用cgdb)
- 开始:gdb fille
- 退出:q
| 命令 | 作用 |
|---|---|
| l | 列出指定文件的源代码 |
| r | 执行当前程序 |
| n | 单步执行,不进入函数内部 |
| s | 单步执行,进入函数内部 |
| b 行 | 在该行处设置断点,不加行数字则默认第一行(如果是b+函数名则在函数第一行达断点) |
2.2 断点之用途
断点是调试的核心,通过在断点的设置可以实现确认错误发生区域的功能,在gdb中对断点的设置有多种形式,现在为大家列出。
(gdb) b Sum #在函数Sum第一行打上断点 Note: breakpoint 2 also set at pc 0x400587. Breakpoint 3 at 0x400587: file mycmd.c, line 4.查看断点:
(gdb) info b #查看断点 Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000004005b5 in main at mycmd.c:12 breakpoint already hit 1 time 2 breakpoint keep y 0x0000000000400587 in Sum at mycmd.c:1 3 breakpoint keep y 0x0000000000400587 in Sum at mycmd.c:4 (gdb) d b 3 #删除第三个断点 Ambiguous delete command "b 3": bookmark, breakpoints. (gdb) d b 2 Ambiguous delete command "b 2": bookmark, breakpoints. (gdb) b 13 #删除之后如果再添加新的断点的话那么会考虑已删去的排序 Note: breakpoint 1 also set at pc 0x4005b5. Breakpoint 4 at 0x4005b5: file mycmd.c, line 13.对于断点的删除与禁用
(gdb) d breakpoints #删除所有断点 Delete all breakpoints? (y or n) y (gdb) info b #查看当前断点 No breakpoints or watchpoints. (gdb) disable breakpoints #禁用当前断点 (gdb) enable breakpoints #启用当前断点2.3 程序的运行与暂停
先给大家列出一些命令
| 命令 | 功能 | 示例 |
|---|---|---|
| c | 从当前位置执行,直到下一个断点或程序结束(continue) | |
| n | 单步执行(不进入函数内部)逐过程 | |
| s | 单步执行(进入函数内部)逐语句 | |
| finish | 执行完当前整个函数,停在函数返回之后,回到调用这个函数的地方。 | |
| u 行 | 快速执行到指定行(不加行则直接跑完整个循环,停在循环外面那行) |
逐过程与逐语句的区别
next : 逐过程
执行下一行源码,如果这一行有函数调用,直接把整个函数跑完,不进入函数内部,停在当前函数的下一行。
(gdb) n # 执行一行,遇到函数直接跳过进去的过程 (gdb) n 3 # 一次性连续执行3行step(s):逐语句
执行下一行源码,遇到自定义函数,会进入函数内部,停在函数第一行。
注意:
printf这种库函数默认不会进。
此外在调试时还有一个常显示的功能:(比如我们要看for循环执行到第几层了)
可以使用 p
(gdb) p i #print i $1 = 7 #左边的1表示这是第一次print的数但是这样的话我们每次想看都需要使用p,如果想要每次执行都能够自动显示的话:
(gdb) display i #每次都打印i 3: i = 13 (gdb) n 3: i = 13 (gdb) n 3: i = 14 (gdb) n 3: i = 14 (gdb) undisplay i #注意这种方式不对,后面应该接监视项的编号(数字),不能直接写变量名 i。 warning: bad display number at or near 'i' (gdb) undisplay 3 #取消编号为3的display (gdb) n (gdb)当然display还可以显示很多东西比如 i 的地址
(gdb) display &i 1: &i = (int *) 0x7fffffffe428 (gdb) n 1: &i = (int *) 0x7fffffffe428 (gdb) n 1: &i = (int *) 0x7fffffffe428 (gdb) n 1: &i = (int *) 0x7fffffffe428 (gdb)此外在调试中还有一个until:继续执行程序,直到到达指定行号 / 跳出当前函数,然后停下。
但要注意的是不能回退
(gdb) until 12 0x00000000004005dc in main () at mycmd.c:17 #当前代码在17行不能跳到12行 (gdb) u 15 running done, result is: [1-100]=5050 0x00007ffff7a2f555 in __libc_start_main () from /lib64/libc.so.6 (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y The program being debugged has been started already. Starting program: /root/lesson/mycmd I will begin Breakpoint 1, Sum (s=1, e=100) at mycmd.c:4 #断点在第四行当前停在了这里 (gdb) u 10 Sum (s=1, e=100) at mycmd.c:10 #成功运行了 (gdb)查看当前栈帧的所有局部变量
栈帧:每调用一次函数,就会在栈上开辟一块独立内存区域,这块区域就叫栈帧。
(gdb) info locals start = 1 end = 100 n = 32767修改变量的值:
# set var 变量名 = 值 set var n = 0 #将局部变量改为02.4 堆栈跟踪
当程序崩溃时,堆栈跟踪时定位问题的关键,gdb中 backtrace可以查看函数的调用链
(gdb) bt #0 0x00000000004005dc in main () at mycmd.c:17核心规则
#0永远是当前正在执行的函数- 编号数字越大,代表越早调用的外层函数
- 函数 return 之后,它的栈帧直接消失,bt 里面就看不到这个函数了
三、gdb中的三种调试技巧
3.1 变量监测(watch)
作用:watch 变量,当这个变量的值被修改时,程序自动停下。
(gdb) watch i # 监测for循环中i的变化 Hardware watchpoint 2: i (gdb) n (gdb) n Hardware watchpoint 2: i #i变化时立刻打印出来了 Old value = 2 New value = 3 0x00000000004005a0 in Sum (s=1, e=100) at mycmd.c:6注意:
- watch 只能监测当前栈帧能访问到的变量如果你已经
finish退出 Sum 函数,Sum 的局部变量n栈帧销毁,不能再 watch n。 👉必须在进入 Sum 函数、栈帧还存在的时候设置watch n。 - 局部变量出了函数就失效,全局变量可以全程 watch。
3.2 set var
作用:调试时,直接修改内存里变量的值,不改动源码文件。
可以直接确定并验证问题的原因
适用场景:
场景 1:修复未初始化的垃圾值
int n; // 没初始化,栈上是随机垃圾值32767gdb 进入 Sum 后:
info locals set var n = 0把垃圾值手动清零,继续单步调试,看程序正常逻辑。
场景 2:强制触发 if/else 分支
假设代码:
if(n > 10) { // 分支A } else { // 分支B }调试时程序走到else,你想直接看分支 A的执行效果:
set var n = 20 n直接修改 n,强制跳进 if 分支,不用改源码。
场景 3:模拟异常情况
比如判断ret = malloc(1024),想模拟 malloc 失败返回 NULL:
set var ret = NULL直接让后面代码走内存分配失败的逻辑,很难手动构造的场景一键模拟。
场景 4:跳过循环,直接修改循环控制变量
while(cnt < 100) { cnt++; }调试到循环中间,不想一步步循环,直接:
set var cnt = 100下一次循环判断直接退出,快速跳出循环。
3.3 条件断点
当需要满足特定情况时触发断点可以使用条件断点
(gdb) b 9 if i==20 #在第九行添加断点,注意是两个等号 Breakpoint 3 at 0x4005a8: file mycmd.c, line 9. (gdb) info b Num Type Disp Enb Address What 3 breakpoint keep y 0x00000000004005a8 in Sum at mycmd.c:9 stop only if i==20 (gdb) condition 3 i==30 #给三号断点追加条件,会覆盖已有条件,也可给已有断点添加条件 (gdb) info b Num Type Disp Enb Address What 3 breakpoint keep y 0x00000000004005a8 in Sum at mycmd.c:9 stop only if i=30上述就是本期的全部内容,对于git工具的用法我会在后续再出一个更加详细的内容~