前言
作为 Linux 开发的 “左膀右臂”,Git 管版本、gdb 调程序 —— 前者搞定代码的迭代与协同,后者专治程序里的各种 “疑难杂症”。这篇博客就从 Git 的核心概念、基础操作,讲到 gdb 的调试指令,把这俩工具的高频用法讲透,帮你把开发效率直接拉满。
📚 Linux 入门篇
🔧 Linux 工具篇
目录
一、Git 版本控制器
1.1 什么是 Git?
【问题】:什么是分布式?
【问题】:什么是集中式?
1.2 Git 核心概念
1.3 GitHub 与 Gitee
二、Git 基础操作
【git log命令】
【git status命令】
【.gitignore文件】
三、gdb前置知识
【readelf指令】
四、gdb相关调试指令
4.1 进入gdb和退出gdb指令
4.2 查看源代码指令
4.3 run/r指令
4.4 断点相关调试指令
4.4.1 break/b指令
4.4.2 d 指令
4.4.3 disable/enable breakpoints 指令
4.4.4 info breakpoints 指令
4.5 next/n 指令
4.6 step/s 指令
4.7 finish 指令
4.8 print/p 指令
4.9 set var指令
4.10 continue(或 c)指令
4.11 display指令
4.12 undisplay指令
4.13 until指令
4.14 breaktrace (或 bt)指令
4.15 info (i) locals指令
Git 是一款分布式版本控制系统,可以理解为代码的 “时光机”—— 它能记录代码的每一次修改,让你随时回退到历史版本,还能支持多人协作开发。和传统的 SVN(svm只能通过服务端查看历史记录) 相比,Git 是 “客户端 - 服务端一体” 的设计,既可以本地单机管理代码,也能通过远程仓库(如 GitHub、Gitee)实现团队协作。
【分布式的核心概念】: 将整体的任务、数据或功能分散部署到多个独立的节点(如计算机、服务器等)上,使各节点能并行处理任务;每个节点可拥有完整或部分资源,支持独立工作,节点之间通过网络进行协同与同步,无需依赖单一中心节点来驱动整个系统运行。 【分布式的典型特点】:
简单类比(以 Git 为例)
Git 作为分布式版本控制系统,核心特点是每个用户的本地都拥有完整的版本库:
【集中式的核心概念】: 将系统的任务、数据或功能集中部署在单一的中心节点(如中央服务器)上,所有操作(如数据存储、任务处理)都依赖该中心节点完成;用户或从属节点需连接到中心节点才能开展工作,本地通常仅保留临时的工作副本,不具备独立处理核心任务的能力。 【集中式的典型特点】:
简单类比(以 SVN 为例)
SVN 作为集中式版本控制系统,核心特点是仅中央服务器保存完整的版本库:
Git 是版本控制工具,而 GitHub、Gitee 是基于 Git 的商业化代码托管平台—— 你可以把它们理解为 “代码的社交网络”,既能存代码,又能和全球开发者协作、学习优质项目。类似地,基于 Linux 内核也衍生出了 CentOS、Ubuntu 等商业化操作系统,原理是相通的~
yum install git,Ubuntu 用 apt install git;【新建仓库】:
进入gitee后点右上角的+,点击后就能看到新建仓库这一选项,点击后就到了下述界面

在这里,选择你需要的代码语言来保存版本。README文件用于仓库的说明,创建时会自动生成中英文版本。在分支选项中,这里推荐选择单分支是为了简化项目管理,特别是在项目初期或只是用于代码托管时,不需要复杂的分支结构。单分支模式更适合简单的版本控制,避免了多分支带来的合并冲突和管理开销。
【将远端仓库拉到本地】:
点击新建仓库右上角的克隆和下载:


克隆成功!!!
#将远端仓库克隆到本地的命令
git clone 仓库地址
Test仓库此时就在我们的当前目录下,大家使用时尽量放在普通用户下,我这只是示范,所以就没有切换用户了。
当我们将远端仓库拉取到本地,为了确保提交的代码身份被Gitee/GitHub正确识别,需要在本地进行相关配置
git config --global user.email "你的邮箱"
git config --global user.name "你的名字"Git 配置 user.name 和 user.email,是为了给代码提交打上专属身份标签,方便后续查看提交历史、追溯修改责任。
【第一招】:**git add**—— 把文件加入 “待提交队列”把需要版本管理的文件告知 Git,命令格式:
git add 文件名 # 单个文件
git add . # 当前目录所有文件
【第二招】:**git commit**—— 提交修改到本地仓库把 “待提交队列” 的修改保存到本地仓库,同时写提交日志(描述这次改了啥,这块不能随便填写):
git commit -m "提交日志:比如‘新增登录功能’"
【第三招】:**git push**—— 同步修改到远程仓库把本地仓库的修改推送到 GitHub 等远程仓库,命令:
git push
首次推送可能需要输入 GitHub 账号密码(或配置 SSH 密钥免密,新手不建议配置免密),在圈的地方输入你的账号和密码即可,账号会回显,密码不会回显。
【配置免密码提交】:https://blog.csdn.net/camillezj/article/details/55103149
Git 工作流程围绕工作区、暂存区、本地仓库三个区域展开,操作需在仓库目录内进行:
git add 命令,将工作区的修改加入暂存区(可理解为 “待提交清单”);git commit 命令,将暂存区内容提交到本地仓库(.git 目录是本地仓库的核心,提交后形成版本记录并保存);git push 可将本地仓库的变更同步到远程仓库。【git log命令】 作用:查看历史提交记录undefined

历史提交记录的信息,包含你的信息、日志、提交时间等等
【git status命令】 作用:查看当前目录状态

显示当前没有东西可以提交的内容,工作区已清理
【.gitignore文件】 作用是指定 Git 版本控制中需要忽略的文件或目录,让这些文件 / 目录不被纳入版本管理,简单来说就是去掉杂项文件,只保留需要使用的文件

在 .gitignore 文件中,通过通配符(如 *.exe、*.out)可以指定 “匹配该后缀的所有文件都被忽略,不推送到远程仓库”。如果要添加自定义的杂项文件,需要用 *后缀 的格式(比如要忽略所有 .tmp 临时文件,就写 *.tmp);如果只写后缀(如 tmp),Git 会把它当作 “名为 tmp 的文件” 来处理,无法实现批量忽略同后缀文件的效果。
【如何确认gcc/g++默认编译出来的结果是release版本还是debug版本】
没带 "-g" 选项编译出来的结果:

带 "-g" 选项编译出来的结果:

可以看到两次编译后的文件大小不同,那么带 "-g" 选项编译后的可执行程序多了什么呢?
多出来的就是调试信息,这样也可以间接证明gcc默认编译出来的结果是release版本
【readelf指令】
readelf [选项] [ELF文件]-h 显示 ELF 文件头信息-l 显示程序头表信息-S 显示节头表信息-s 显示符号表信息-d 显示动态段信息-w 显示调试信息--help 查看所有选项说明
在 Linux 系统中,虽然可执行文件是二进制格式,但其内部结构采用了 ELF (Executable and Linkable Format/可执行与可链接格式)格式,可以通过 readELF 工具查看。

list / l 行号:显示指定行号附近的源代码,从上次位置继续向下列,每次列 10 行。list / l 函数名:列出指定函数的源代码。注意:gbd会默认记录最近一次的指令,可以回车执行最近一次指令。
【用行号查看演示】:

l 5就是显式第五行上面的5行和下面的5行也就是每次显式10行

输入一次指令后,按回车就会自动向下再输出10行,输出完就显示有多少行

这个时候想要再从头看可以输入l 0,一般从头开始的话,只输输入 l 的话会自动输出10行
【查看函数演示】:

显式sum函数附近的10行代码,反正也是每次输出10行,按空格就继续输出10行,输出完就显示一共多少行
作用:运行程序。 核心特点:
b 行号 或 b 函数名**)** 来实现调试暂停,进而逐步分析程序流程。
没打断点就一次性跑完了,要像vs那样调试的话得必须打断点,确实有点烦人,不好用!!!
这是 GDB 调试中用于设置断点的
break(简写b)指令,有四种常见用法:
break 行号**(或** b 行号**)**:在代码的指定行设置断点,程序运行到该行时会暂停。例如 break 20(或 b 20),会在第 20 行设置断点。break 函数名**(或** b 函数名**)**:在指定函数的开头设置断点,程序进入该函数时会暂停。例如 break main(或 b main),会在 main 函数的开头设置断点。break test.c:20(或 b test.c:20),会在 test.c 文件的第 20 行设置断点。break test.c:sum(或 b test.c:sum),会在 test.c 文件的 sum 函数开头设置断点。break 指令会默认在当前调试的主文件(通常是包含 main 函数的文件)中设置断点分别在20行和main函数处打断点:

如果在第 20 行或main函数入口设置断点时,该行是空行,GDB 会自动定位到下方最近的有效语句处暂停执行,main函数的情况同理。
指定文件打断点:

这是 GDB 调试中用于删除断点的 delete breakpoints 指令(简写d),有两种常见用法:
n的断点。
第一个指令是删除序号为3的断点,第二个指令是删除所有断点,Num下的编号就对应了每个断点的序号
功能 | 完整指令 | 简写指令(常用) | 示例 |
|---|---|---|---|
禁用指定断点 | disable breakpoints 序号 | disable 序号 /dis 序号 | (gdb) dis 1(禁用序号 1 的断点) |
禁用所有断点 | disable breakpoints | disable / dis | (gdb) dis(禁用所有) |
启用指定断点 | enable breakpoints 序号 | enable 序号 /en 序号 | (gdb) en 1(启用序号 1 的断点) |
启用所有断点 | enable breakpoints | enable / en | (gdb) en(启用所有) |
禁用断点后就不会生效了
这是 GDB 调试中用于查看断点信息的 info breakpoints(简写
info(或i) breakpoints)指令:

列名 | 核心含义(图标版) |
|---|---|
Num | 🔢 断点序号(操作标识),序号呈线性增长,前面用过的序号纵使删除当前序号指定的断点,依旧不会用到 |
Type | 📌 断点类型(普通 / 观察等) |
Disp | ⚙️ 处置方式(保留 / 自动删) |
Enb | 🚦 启用状态(y = 启用 /n = 禁用) |
Address | 📍 内存地址(机器码位置) |
What | 📄 具体位置(文件 + 行号 / 函数) |
这是 GDB 调试中用于单条执行代码的 next(简写 n)指令:

当前运行到第一个断点(main 函数处),输入n执行两次后却进入了sum函数,按理next不该进入函数才对?

这是因为 sum**函数内部设置了断点**,next执行sum函数调用时,会在函数的断点处暂停(此为断点触发的暂停,并非next主动进入函数内部单步)。
这是 GDB 调试中用于逐语句执行代码的 step(简写 s)指令:

这是 GDB 调试中用于执行到函数返回的 finish 指令:
在**main**函数中用**finish**,没有实际调试作用,不能这么用~

在sum函数中用会直接跳到函数调用处

print/p 指令可打印表达式值,支持修改变量(修改后并打印)、调用函数;
p 变量是其简写形式,用于快速打印单个变量值,二者功能本质一致,p是更简洁的常用操作方式。
GDB 中1 2这类序号,是同一个 GDB 调试会话内 **print/p操作的临时计数标记,按执行顺序线性递增;只有完全退出 GDB 并重新启动调试会话时,序号才会从

临时修改变量值:

直接调用函数,不论函数是否被执行,都会打印该函数的结果

set var 变量名=新值(例如set var a=20,纯修改变量值,执行后无自动输出)
c(例如输入c,程序从当前断点位置继续连续执行,直到遇到下一个断点或程序结束)
在运行后,输入c,无断点就直接运行结束了

display 变量名(例如display a,跟踪查看变量a,程序每次暂停时都会自动显示其当前值)

相当于vs监视窗口
undisplay 序号(例如undisplay 1,取消序号为 1 的变量跟踪;若要取消所有,可多次执行或用delete display系列指令)
until 行号(例如until 50,程序从当前位置连续执行到第 50 行后暂停,适合快速跳过循环等场景),只能往后跳
bt(例如输入 bt,查看当前程序的函数调用栈,包括各级函数的调用关系、参数和返回地址等)

这张图完美展示了函数调用栈(栈帧)随程序执行的动态变化过程:
add** 函数中):调用栈有 3 层(main→sum→add),因为 add 还在执行,没返回 sum。sum** 函数中):add 执行完返回,调用栈变为 2 层(main→sum),因为 sum 还在执行,没返回 main。main** 函数中):sum 执行完返回,调用栈只剩 1 层(main),因为所有子函数都已返回。每一次函数调用会 “压栈”(新增栈帧),函数返回会 “出栈”(移除栈帧),这就是调用栈的动态变化逻辑~
info locals(或i locals,例如输入info locals,查看当前栈帧中所有局部变量的名称和值)
类似于调试器的 **“自动监视窗口”,它会实时显示当前栈帧(即当前执行的函数)中所有局部变量的名称和值 **,只聚焦于程序运行到 “当前代码块” 时的变量状态,非常方便查看函数内局部变量的实时变化。

不是常显示的,运行到下一句就不会显示,需要你再次手动操作