C++工程命令速查

编译不过 / 跑太慢 / 调不了 / 内存出错 / 想发布——按场景反查该敲哪几条命令

通识重叠

◎学完本节你会

本页命令全部本机实测(g++ 15.2 / gdb 17.1 / GNU Make 3.82 / git 2.54,MSYS2 MinGW64)。 每条命令都标了在本机的真实状态:✅ 可用 ⚠️ 本机未装 ❌ 本机不支持。 和 92.2 一样,我不写"大概是这样"的命令。

1我现在要干嘛?——按场景选命令

工程里你不会"背命令",而是遇到一个场景,反查该敲什么。 点下面 5 个常见场景,看每个场景推荐的命令组合和理由。

← 点上面任意一个场景开始

2命令速查表(6 大类)

按类别筛选,或搜关键词(如 -O2、backtrace、strip)。 每条命令都有作用说明,多数附本机实测输出,命令块可整行复制。

3Windows(MinGW) vs Linux:这些命令不一样

同一条命令,换个平台可能就跑不了或产物名不同。下表都是本机实测差异,照抄前先看你用哪个平台。

场景LinuxWindows / MinGW
可执行产物名./hellohello.exe(运行时 .\hello.exe)
make 命令名make本机 make 未装,用 mingw32-make
删除文件rm appdel app.exe(Makefile 的 clean 里)
内存检查 Sanitizer-fsanitize=address 可用本机不支持:报 cannot find -lasan(缺运行时库)
动态库后缀.so.dll
路径分隔符/\(但 g++/git 里一般也用 /)
最坑的一条:Windows 下 ./hello 会报"找不到文件"——因为产物是 hello.exe, 要写 .\hello.exe。这个和 26.1「编译四步」直接相关。

!命令层面的高频坑(全部本机实测踩过)

① 调试时开着 -O2:变量被优化掉,GDB 里 print a 报 No symbol "a" in current context。调试一律用 -O0 -g。
② 断点停在 main 入口就 print 局部变量:此时赋值还没执行,读到的是垃圾值 (本机实测 print x 得 32758)。要先 next 走过赋值语句再看。
③ Makefile 目标名和产物名不一致:写 app: 但编译成 -o app.exe, make 永远找不到目标产物 → 每次都重编(本机实测复现)。目标名必须等于真实产物名。
④ Makefile 用空格代替 TAB:报 missing separator(见 92.2 的 E12)。 很多编辑器的"自动缩进"会偷偷把 TAB 转成空格。
⑤ 在 MinGW 上照抄 Linux 的 Sanitizer 命令:-fsanitize=address 直接链接失败。 Windows 想做内存检查,得用 WSL/Linux,或换 clang,或用 -Wall -Wextra 先抓一部分。

✎练习题

Q1. 调试时 GDB 里 print x 报 No symbol "x" in current context,最可能是?
这是坑①。-O2 会把用不到的变量优化掉,GDB 就找不到符号。调试要用 g++ -O0 -g——-O0 关优化、-g 生成调试符号。
Q2. 发布前想给可执行文件"瘦身"(去掉调试符号),用哪个命令?
strip app.exe 去掉符号表。本机实测:127991 bytes → 17408 bytes,瘦身约 86%。注意 strip 后就不能再用 GDB 调试了(符号没了),所以调试版和发布版要分开编。
Q3. Windows 下敲 make 提示"不是内部或外部命令",应该?
MinGW 自带的 make 命令名是 mingw32-make,不叫 make。本机实测:make 未安装,mingw32-make(GNU Make 3.82)可用。

Q4. 链接报 undefined reference 时,92.2 教你用 nm -C app.o 查符号。 -C 是干嘛的?nm 输出里符号前的 T 和 U 分别代表什么?

答案:-C = demangle(还原被 C++ 改编过的符号名,把 _Z3addii 显示成 add(int,int))。
T = 该符号在本文件的代码段里已定义(Text,大写=全局);U = 未定义(Undefined), 即"本文件用到了它,但定义在别处,要靠链接补上"。本机实测:nm -C mk.o 输出 T main、U __main。

Q5. 你的 Makefile 每次 make 都重新编译,明明代码没改。列出至少两个可能原因。

答案(本机都实测复现过):
① 目标名 ≠ 产物名:写 app: 却编译成 -o app.exe,make 找不到叫 app 的产物,永远判为过期。
② Makefile 有 BOM 头:某些编辑器(含 PowerShell 的 -Encoding utf8)存文件会加 BOM, make 把 BOM 当成目标名的一部分(-d 调试可见 Considering target file 'app.exe'),于是永远认为文件不存在。
③ 依赖里写了每次都会更新的文件(如某个总被 touch 的头文件)。

Q6. 你在 Windows(MinGW) 上想抓一个数组越界的 bug,照 Linux 教程敲 g++ -fsanitize=address -g app.cpp,却报 ld.exe: cannot find -lasan。 为什么会这样?给出至少两种可行的替代方案。

答案:MinGW 默认不带 ASan 的运行时库 libasan(本机实测:只有个头文件 asan.h,没有链接库), 所以 -fsanitize=address 能编译却链接失败。这不是你命令写错,是平台缺库。
替代方案:
① 换环境:用 WSL / Linux,或 MinGW 装 mingw-w64-x86_64-libasan 包,或改用 clang(clang 的 ASan 在 Windows 支持更好);
② 退而求其次:用 -Wall -Wextra 让编译器静态抓一部分(本机实测能抓到 'a' is used uninitialized 这类, 但抓不到运行时的越界写入);
③ 手动排查:把越界怀疑点的下标打印出来,或用 std::vector::at()(越界会抛 out_of_range)替代 [] 定位。

◎跨学科小贴士:命令行是"工程师的通用语"

你会发现这一章的命令横跨 g++、make、cmake、gdb、git、nm、strip——它们来自不同的工具、不同的年代, 却都遵循同一套命令行惯例:命令 [选项] [参数]、-o 指定输出、--help 查帮助、 退出码 0 表示成功。

这套惯例来自 Unix 哲学(1970 年代):"每个程序只做一件事并做好,程序之间用文本流协作"。 所以 g++ -E ... | grep 宏名 这种把几个工具的输入输出串起来的写法才成为可能—— 这就是管道(pipe),34 章「信号与 IPC」会讲它的原理。

记住这套通用语,你学任何新工具(Docker、Kubernetes、云 CLI)都会发现"哦,还是那一套"。 命令行的肌肉记忆,是工程师最保值的基本功之一。