-m包含系统头文件依赖,-mm仅保留项目自定义头文件依赖;二者均属预处理阶段选项,不触发编译,常配合-mf、-mt、-mp使用以生成可靠makefile依赖规则。

gcc -M 会把系统头文件也列进依赖,-MM 不会
这是最核心的区别。-M 让预处理器输出源文件所有 #include 的头文件路径,包括 <stdio.h></stdio.h> 这类标准库头文件及其层层嵌套的内部头(比如 /usr/include/features.h)。而 -MM 主动过滤掉所有系统头目录下的文件,只保留项目里自己写的头文件(比如 "config.h" 或 "utils.h")。
实际影响很直接:用 -M 生成的 Makefile 依赖规则里混着大量系统路径,一旦系统更新或换机器,make 可能因找不到某个 /usr/include/bits/... 而误判“需要重编译”,或者在交叉编译时彻底失效。而 -MM 输出干净,只反映你代码的真实依赖。
为什么不能只靠文件名判断 -M 还是 -MM
#include <xxx></xxx> 和 #include "xxx" 在 -MM 下行为一致——是否被包含,只看头文件最终落在哪个目录。如果 "log.h" 实际位于 /usr/local/include/log.h(被加进了系统头搜索路径),-MM 也会把它过滤掉;反之,如果 <mylib.h></mylib.h> 是通过 -I./include 指定的本地路径,-MM 就会保留它。
常见误区是以为双引号就一定被保留、尖括号就一定被忽略。不是这样。关键在 GCC 的头文件搜索顺序和 -I 参数指定的路径是否属于“系统目录”(由编译器内置定义,通常指 /usr/include 及其子目录)。
实际写 Makefile 时该用哪个
几乎总是选 -MM。理由很实在:
- 生成的依赖文件(如
main.d)体积小、可读性强 - 避免因系统头变化触发无意义的重编译
- 配合
-MF和-MP使用更可靠(例如:gcc -MM -MF main.d -MP main.c) - CI/CD 环境或不同 Linux 发行版之间迁移更稳定
只有极少数调试场景才用 -M,比如你想确认某个标准头到底引入了哪些底层定义,或者排查宏展开路径。
容易踩的坑:-M/-MM 必须配合 -E 或显式调用预处理器
-M 和 -MM 是预处理阶段选项,**不会触发编译或链接**。单独运行 gcc -M main.c 是合法的,但如果你加了 -c 或 -o,GCC 会忽略 -M 直接走编译流程,什么依赖都不会输出。
正确姿势是:
- 生成依赖时:用
gcc -MM -MF deps.d main.c - 想看结果又不写文件:用
gcc -MM main.c(输出到 stdout) - 不要混用:
gcc -c -MM main.c→ 无效,-MM被静默丢弃
另外,-M 和 -MM 默认输出目标名是 main.o: main.c ...,如果源文件带路径(如 src/main.c),得用 -MT 显式指定目标名,否则 Makefile 解析会出错。











