news 2026/10/8 22:39:01

GCC 和 GDB命令简单解读

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GCC 和 GDB命令简单解读

最近在带一个刚接触 Linux 的朋友过 C 的开发流程,发现编译和调试这两个环节,看着简单,真上手的时候一堆小细节会把人卡住。趁这个机会把我自己踩过的坑整理一下,也当是给自己留个备忘。

编译到底发生了什么

很多人写 C 就是gcc test.c -o test一条命令跑完,能出结果就完事了。但一旦中间出错,或者要分步看中间产物,就得搞清楚这条命令背后其实是四步。

我习惯把这四步对应到四个后缀,记起来不容易混:

  • 预处理(-E):把#include的头文件展开、宏替换掉,输出.i
  • 编译(-S):把 C 源码翻译成汇编,输出.s
  • 汇编(-c):把汇编转成机器码的目标文件,输出.o
  • 链接:把目标文件和库拼成可执行文件

这里有个特别容易栽跟头的地方:-S是大写,-c是小写。我第一次用的时候就记反了,gcc -s出来的是汇编,还纳闷半天为什么没生成目标文件。这个坑到现在我还得靠"S对应 aSsembly(汇编)"来提醒自己。

拿一段最简单的代码试一下四个参数,感受最直观。先写个test.c:

#include<stdio.h>intmain(){printf("hello\n");return0;}

然后依次敲这四条,看每一步生成了什么文件:

gcc-Etest.c-otest.i# 预处理:test.i 里能看到 stdio.h 被展开成一大片gcc-Stest.c-otest.s# 编译:test.s 是汇编,能看到 call printfgcc-ctest.c-otest.o# 汇编:test.o 是二进制目标文件gcc test.o-otest# 链接:test 才是能 ./test 跑的可执行文件

跑完ls一下,test.i、test.s、test.o、test四个文件都在,比背概念清楚多了。

另一个更值得较真的点是,编译阶段的输入到底是.c还是.i。严格说,.c要先经过预处理变成.i,.i才是编译这一步真正的输入:

test.c --预处理--> test.i --编译--> test.s --汇编--> test.o --链接--> test

你写gcc -S test.c能一步出.s,不是因为跳过了预处理,而是 gcc 在内部顺手把预处理做了。要较真的话,应该显式分两步:

gcc-Etest.c-otest.i# 预处理,得到 .igcc-Stest.i-otest.s# 把 .i 编译成 .s

平时我们不会真的这么分步,但搞清楚这个顺序,对理解"为什么有时候报错报在头文件里"很有帮助——因为那正是预处理阶段展开头文件时出的问题。

想验证每一步的产物对不对,可以用file看一眼:

filetest.i# C source, ASCII textfiletest.s# assembler sourcefiletest.o# ELF relocatable object filefiletest# ELF executable

一目了然,比空想管用。

一次完整的调试流程

光会单个命令没用,得知道整条流程怎么串起来。我拿前面那个求平方和的程序走一遍。假设源码sum.c长这样:

#include<stdio.h>intprint(intnum){intret=num*num;returnret;}intmyfunc(intnum){inti=1,sum=0;while(i<=num){sum+=print(i);i++;}returnsum;}intmain(){intnum=0;scanf("%d",&num);intresult=myfunc(num);printf("%d",result);return0;}

第一步,编译的时候带上-g,这步省了后面全废:

gcc-gsum.c-osum

第二步,进 gdb 把这个程序挂上去:

gdbsum

进去之后,(gdb)提示符就等着你下命令了。我一般先看两眼源码,确认行号,再设断点:

(gdb)list# 看看源码带行号(gdb)breakprint# 在 print 函数入口设断点

第三步,跑起来:

(gdb)run

程序会卡在scanf,敲5回车。然后第一次调用print(1)时命中断点,gdb 会停下并显示类似:

Breakpoint 1, print (num=1) at sum.c:3 3 int ret = num * num;

第四步,单步走,边看变量:

(gdb)next# 执行完 ret = num * num 这行(gdb)print num# 看 num 是几(gdb)print ret# 看 ret 算出来是多少(gdb)continue# 放行,下一次循环进 print(2) 会再停

print被调了 5 次,断点会命中 5 次,每次continue放行一次。到最后程序输出结果 55,gdb 提示[Inferior 1 (process ...) exited normally]。

第五步,看完退出:

(gdb)quit

这套「-g编译 →gdb挂载 →list看源码 →break设断点 →run跑 →next/print单步看变量 →continue放行 →quit退出」的顺序,是我日常调试的标准动作,练熟了后面查 bug 都是这套路子的变形。

调试时最容易懵的两个瞬间

gdb 这东西,最吓人的不是报错,而是"没反应"。我有一次调试一个带scanf的程序,run之后屏幕停在(gdb)提示符,我还以为程序死锁了。其实根本不是——程序跑到scanf("%d", &num),正等着我敲数字。

这种时候直接输个数字回车就行,比如:

(gdb) 5 # 喂给 scanf (gdb) print num # 看看读进来没有 (gdb) continue # 继续往下走

拿前面那个求平方和的例子说:程序里main有一句scanf("%d", &num),run起来后光标停在那,不是卡了,是它正在等你输入。敲5回车,程序才继续往下跑,第一次进入print函数时就命中你设的断点。后来想想这事挺蠢的,但第一次遇到的人十个有八个会愣住。

另一个高频问题是:设断点的时候不知道那行代码在第几行。我在没有行号的场景下会优先用函数名设断点,直接绕开行号:

(gdb) break print # 在 print 函数入口停 (gdb) break main # 在 main 入口停

如果确实想按行号来,list命令能直接列出带行号的源码,看清楚了再break 行号:

(gdb) list # 列出当前函数的代码 (gdb) list 1,50 # 列出第 1 到 50 行

比如list出来长这样:

1 #include <stdio.h> 2 int print(int num){ 3 int ret = num * num; 4 return ret; 5 }

一看就知道print那行在第 3 行,直接break 3就行。

不过所有这些的前提是编译时加了-g。忘了加-g的话,list会告诉你没有符号表,源码都看不到,断点也就无从谈起。所以我自己的习惯是:只要是为了调试编的,一定加-g,不省这一下。

常用命令记这么几个就够日常用了:

命令缩写干什么
nextn单步,不钻进函数
steps单步,钻进函数
print 变量p 变量看变量值
continuec跑到下一个断点
bt—看调用栈
quitq退出

那个No such file or directory

这个报错也值得单独说一句。./sum跑不起来,报No such file or directory,大部分人第一反应是"文件没了",其实更多时候是根本没编译出sum这个可执行文件。

排查就三步:先ls -l看目录里到底有什么,再gcc sum.c -o sum编译,最后./sum跑。特别要注意的是,gcc sum.c不带-o的话,默认产物叫a.out,这时候你跑./sum当然找不到——文件压根不叫这名。

举个最典型的翻车现场:你以为已经编译好了,直接./sum,结果:

$ ./sum -bash: ./sum: No such file or directory

跑个ls一看,目录里只有sum.c,压根没有sum。要么是忘了编译,要么是gcc sum.c没加-o sum,产物叫a.out去了。补一句gcc sum.c -o sum,再./sum就通了。


编译和调试这块,说穿了就是几个反直觉的细节:-S/-c的大小写、.i和.c谁是编译的真输入、程序"卡住"其实是在等输入、忘了-g就看不到源码。这些坑踩过一次就记住了,写下来希望有人能少踩几个。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 22:38:08

传统文化视频的本地素材替换流程:从画面重复到成片差异化

如果你正在做历史解说或诗词赏析视频&#xff0c;大概率遇到过同一个问题&#xff1a;文案写到「赤壁之战」时&#xff0c;市面上的视频素材翻来覆去就那几段影视剧镜头&#xff1b;写到「大漠孤烟直」时&#xff0c;能用的风光画面也就那几帧。画面重复不只是观感问题&#xf…

作者头像 李华
网站建设 2026/10/8 22:34:55

不用配置环境!Codex 整合安装包,Windows/Mac 一键部署教程

前言 相比手动安装 Node.js、再敲命令行&#xff0c;一键安装包将环境配置、依赖安装与客户端部署一并打包&#xff0c;省去了繁琐的搭建步骤&#xff0c;非常适合不想折腾、希望快速上手的用户。全程只需按提示操作&#xff0c;几分钟内即可开始使用。 一、下载安装包 打开…

作者头像 李华
网站建设 2026/10/8 22:34:54

工业电源保护实战:电子保险丝加MCU策略打造热插拔无忧方案

前阵子调试一块 12V 工业控制板&#xff0c;客户反馈说工人带电把电源接头拔了又重新插上&#xff0c;板子偶尔会黑屏甚至烧掉后级器件。查到最后&#xff0c;问题出在热插拔瞬间的浪涌电流和触点抖动&#xff1a;输入端几十微法的电容被瞬间充电&#xff0c;电流尖峰直接把后级…

作者头像 李华
网站建设 2026/10/8 22:33:29

frp 内网穿透

阿里云服务器linux 操作系统 本地window 先下载frp :Releases fatedier/frp GitHub 第一部分&#xff1a;阿里云 Linux 部署 frps&#xff08;服务端&#xff09; 1.1 上传解压 通过 xshell/ssh 登录阿里云 ECS # 放到/usr/local目录 cd /usr/local# 上传压缩包到此目录…

作者头像 李华
网站建设 2026/10/8 22:30:49

智能对话压缩技术:用 PostgreSQL 持久化 AgentState 的上下文管理革新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 22:27:43

AI编程工具选型指南:Codex、opencode、dsh、Agent Network、PI-Desktop对比

1. 五个工具摆在面前&#xff0c;先搞清楚它们各自站什么位置第一次看到 Codex、opencode、dsh、Agent Network、PI-Desktop 这五个名字放在一起&#xff0c;很多人第一反应是“不都是 AI 编程工具吗&#xff0c;随便挑一个用不就行了”。我一开始也这么想&#xff0c;直到把五…

作者头像 李华