发布时间:2026/7/23 3:21:20
Makefile精读 · M03-03 | 自动变量:`$@` `$<` `$^` `$?` `$*` 与目录变体 模块M03 变量体系篇号M03-03 / 41预计阅读4045 分钟主线GNU Make加厚篇★文章目录本篇目标30 秒速览正文1. 自动变量是什么2. 核心五件套2.1 $ — 当前目标2.2 $ — 第一个依赖prerequisite2.3 $^ — 所有依赖去重2.4 $ — 所有依赖保留重复2.5 $? — 「过期相关」的依赖2.6 $* — 茎stem3. 一张表背下来4. 目录与文件名变体5. 和模式规则一起读加厚6. 常见坑6.1 在配方外使用6.2 $ 不是「唯一源文件」而是「第一个依赖」6.3 $^ 包含不想链进的文件6.4 混淆 $? 与 $^6.5 $* 迷信7. 实战对照手写名 vs 自动变量8. 次常用$|、$%扫盲9. 对照表可贴显示器读 Makefile 检查清单练习判断题对 / 错改错题动手题下一篇预告本篇目标吃透规则配方里最常见、却从不需要你赋值的符号自动变量。掌握$$$^$?$*的精确含义以及$(D)/$(F)等目录变体。学完本篇模式规则与链接行终于能读能写不再靠「抄别人 Makefile」蒙混。30 秒速览自动变量只在配方中有意义由 Make 在执行该规则时填入。$当前目标$第一个依赖$^所有依赖去重$?比目标新的依赖$*模式规则的茎。链接常用$(CC) -o $ $^编译常用$(CC) -c $ -o $。目录变体$(D)/$(F)目标的目录/文件名$^亦有$(^D)等少见但要认得。$*在非模式规则里常为空或不靠谱——茎相关逻辑优先信任模式/静态模式。写死文件名能跑但换目标就炸自动变量让一条规则服务一类文件。正文1. 自动变量是什么它们不是你在文件开头:出来的配置而是 Make 在执行某条规则的配方时临时提供的app: main.o util.o gcc -o $ $^执行更新app时自动变量值$app$^main.o util.o$main.o第一个没有自动变量时你得写gcc -o app main.o util.o一换目标名或依赖列表配方就要改——模式规则更是几乎离不开自动变量。作用域记忆自动变量为「当前正在执行的这条规则」服务写在配方外通常无意义值为空或不符合预期。2. 核心五件套2.1$— 当前目标hello: hello.c gcc -o $ hello.cmake hello→-o hello。模式规则里更关键一条配方生成「正在构建的那个文件」。2.2$— 第一个依赖prerequisite%.o: %.c gcc -c $ -o $目标$$main.omain.cmain.o编译规则几乎总是$→ 源文件$→ 目标文件。若依赖写成%.c common.h$仍是第一个即对应的.c不会变成common.h顺序很重要。2.3$^— 所有依赖去重app: main.o util.o main.o gcc -o $ $^$^是main.o util.o重复的main.o去掉。链接行圣经$(CC) -o $ $^2.4$— 所有依赖保留重复与$^类似但不去重。少数工具链对链接顺序/重复库敏感时可能用到。一般优先$^。2.5$?— 「过期相关」的依赖比目标更新的那些依赖目标不存在时通常是全部依赖。适合「只处理变化的输入」lib.a: a.o b.o c.o ar r $ $?含义直觉只把比lib.a新的.o更新进库。日常编译/链接更常用$^读旧库维护规则时会见到$?。2.6$*— 茎stem在模式规则%.o: %.c更新path/foo.o时$*为path/foo。%.o: %.c echo stem$* gcc -c $ -o $显式规则无模式里$*常常为空——不要在普通hello: hello.c上依赖$*。需要茎时用模式/静态模式。3. 一张表背下来对规则build/app: main.o util.o假设正在更新build/app且util.o比build/app新、main.o不新变量典型值口诀$build/app目标全家桶路径$main.o第一个依赖$^main.o util.o所有依赖去重$同$^本例无重复所有依赖保留重复$?util.o比目标新的依赖$*非模式时勿用模式茎编译模式%.o: %.c $(CC) $(CFLAGS) -c $ -o $链接显式app: $(OBJS) $(CC) $(LDFLAGS) -o $ $^ $(LDLIBS)4. 目录与文件名变体目标或依赖带路径时常用「拆目录 / 拆文件名」变量含义若$build/tmp/app$(D)目标的目录部分build/tmp$(F)目标的文件名部分app$(D)/$(F)第一个依赖的目录/文件名同理$(*D)/$(*F)茎的目录/文件名模式规则时经典用途确保输出目录存在build/%.o: src/%.c mkdir -p $(D) $(CC) -c $ -o $也可mkdir -p build但$(D)在多层子目录build/foo/bar.o时更通用。说明若目标无目录app$(D)为.当前目录。还有$(^D)、$(^F)等对「所有依赖」的拆分少见认得即可。老代码可能用$(dir $)/$(notdir $)函数M04达到类似效果。5. 和模式规则一起读加厚bin/%: src/%.c mkdir -p $(D) $(CC) $(CFLAGS) -o $ $make bin/hello时变量值$bin/hello$src/hello.c$*hello$(D)bin静态模式同样适用$(OBJS): build/%.o: src/%.c mkdir -p $(D) $(CC) -c $ -o $阅读口诀先定当前目标 → 填$→ 看规则依赖列表如何展开 → 填$/$^→ 若有%再填$*。6. 常见坑6.1 在配方外使用TARGET : $ # 通常得不到「某个规则的目标」解析阶段没有「当前规则目标」的完整上下文别这么存。6.2$不是「唯一源文件」而是「第一个依赖」%.o: common.h %.c gcc -c $ -o $$是common.h编译会错。应把源文件依赖写在最前%.o: %.c common.h gcc -c $ -o $6.3$^包含不想链进的文件若把头文件或生成脚本误列进依赖$^会把它们丢给链接器。链接依赖应只列.o/库头文件用附加依赖规则挂上M02-02 / M06。6.4 混淆$?与$^$?只含过期依赖完整链接一般需要$^否则可能漏掉未重编的.o它们仍应参与链接。ar r $ $?是库更新的特殊习惯不要照搬到gcc -o $ $?。6.5$*迷信非模式规则里打印$*为空不代表 Make 坏了。需要「去后缀名」时用$(basename $)或模式茎。7. 实战对照手写名 vs 自动变量# 僵硬 app: main.o util.o gcc -o app main.o util.o # 灵活 app: main.o util.o gcc -o $ $^ # 再进一步 OBJS : main.o util.o app: $(OBJS) gcc -o $ $^改OBJS时配方不用动——这就是自动变量的工程价值。8. 次常用$|、$%扫盲变量含义$|order-only 依赖M06-03不参与「是否过期」的时间戳比较$%当目标是archive(member)形式时的成员名现代少手写本篇以五件套 目录变体为主见到$|知道去 M06 即可。9. 对照表可贴显示器$ 目标 $ 首依赖 $^ 全依赖去重 $ 全依赖保留重复 $? 比目标新的依赖 $* 模式茎 $(D) $(F) 目标的目录 / 文件名编译-c $ -o $链接-o $ $^建目录mkdir -p $(D)读 Makefile 检查清单配方里的$$$^能否还原成具体文件名$对应的是否真是「源文件」依赖顺序对不对链接行用的是$^还是误用$?带输出目录时是否用$(D)/mkdir -p$*是否出现在非模式规则中可疑练习判断题对 / 错$表示所有依赖。模式规则%.o: %.c中更新foo.o时$为foo.o$为foo.c。链接命令写成gcc -o $ $?与gcc -o $ $^在任何情况下等价。$(D)在目标为build/a.o时是build。参考答案错。$是第一个依赖全部依赖是$^。对。错。$?只含过期依赖完整链接通常要用$^。对。改错题%.o: headers.h %.c gcc -c $ -o $ app: main.o util.o gcc -o $ $?指出两处问题并改正。参考答案$会变成headers.h应把%.c放在依赖首位。链接应使用$^而不是$?。%.o: %.c headers.h gcc -c $ -o $ app: main.o util.o gcc -o $ $^动手题在模式规则配方第一行加echo $ $ $^ $*, 构建两个.o观察输出。写build/foo.o规则打印$(D)$(F)。打开任意真实项目 Makefile找到一处-o $与一处-c $还原其具体文件。下一篇预告M03-04《目标/模式特定变量、export与环境变量边界》—target: VAR : value、模式特定变量以及 Make 变量如何进到配方里的 shell 环境。学完 M03-04M03 模块收官变量体系闭环。

相关新闻

2026/7/23 3:21:20

AI模型调用成本优化:OpenRouter智能路由策略与工程实践

最近在折腾几个 AI 项目时,我发现一个挺有意思的现象:不少团队在模型调用成本上卡住了。不是技术实现不了,而是每次调用都在烧钱,尤其是面对高频、长文本或复杂推理任务时,账单增长速度比代码跑得还快。这时候&#xf…

2026/7/23 4:56:24

SolidWorks英文界面汉化方法与常见问题解决

1. SolidWorks英文界面汉化全攻略刚装完SolidWorks发现全是英文看不懂?别急着重装!作为一款全球通用的三维设计软件,SolidWorks默认安装确实会出现英文界面,但这并不意味着你必须要用英文操作。实际上通过简单的设置就能切换成中文…

2026/7/23 4:56:24

还在为论文头秃?这5个AI论文写作工具让你效率翻倍!

朋友们,写论文是不是让你抓狂?对着空白文档发呆、查重率压不下来、导师催稿催到心慌……别急!今天给大家推荐几款AI工具,只要用对了工具,真的能让你从焦虑中解脱! 不过先说清楚哈——AI不能替你写论文&…

2026/7/23 4:56:24

ChatGPT记忆功能:提升开发者对话效率的AI记忆技术解析

ChatGPT最近推出的记忆功能更新获得了用户广泛好评,这个功能让AI能够记住对话中的重要信息,在后续交流中提供更连贯、个性化的服务。对于经常使用ChatGPT进行编程咨询、技术讨论的开发者来说,这意味着不再需要每次重复项目背景和技术栈&#…

2026/7/23 4:56:24

VC++ DirectShow视频采集实战:从Filter Graph构建到Halcon机器视觉集成

1. 项目概述:为什么DirectShow依然是视频采集的“老炮儿”选择在桌面应用开发,尤其是需要处理音视频流的场景里,DirectShow这个名字对于VC开发者来说,既熟悉又让人有点“敬而远之”。熟悉是因为它曾是Windows平台上多媒体处理的基…

2026/7/23 4:56:24

Claude Code系统提示词优化:80%削减实战与AI编程效率提升

Claude Code 系统提示词削减80%的实战经验在AI编程助手的使用过程中,系统提示词(System Prompt)的优化一直是提升效率的关键环节。最近在实际项目中使用Claude Code时,通过系统性的提示词优化策略,成功将原本冗长的系统…

2026/7/23 4:51:24

采访音视频转文字大揭秘:这三款软件准确率爆表!

一、引言作为一名 AI 博主,日常少不了和各种内容创作打交道。最近,我完成了一次特别有意义的采访,采访过程中录下了珍贵的音视频资料。可采访结束后,我却陷入了苦恼之中 —— 要把这些冗长的音视频转化为文字稿,手动输…

2026/7/22 9:29:13

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/23 0:01:10

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/22 21:00:12

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…