您现在的位置是:亿华云 > 系统运维
Makefile-只修改了.h头文件,编译为什么不起作用?
亿华云2025-10-03 20:27:07【系统运维】1人已围观
简介不知道各位小伙伴是否碰到过这样的情况:一个 .c 文件 include 另一个 .h 头文件,使用 Makefile 来构建(编译)应用程序。第一次编译、执行,很正常!但是此时,如果修改了 .h 头文
不知道各位小伙伴是只修否碰到过这样的情况:
一个 .c 文件 include 另一个 .h 头文件,使用 Makefile 来构建(编译)应用程序。头文
第一次编译、译为用执行,起作很正常!
但是只修此时,如果修改了 .h 头文件,头文再次编译时,译为用就出现问题了:
预期的起作执行流程是:make 发现 .h 头文件的修改时间更新,于是只修重新编译包含这个头文件的所有 .c 文件。
可实际的头文结果却是:make 并没有识别出 .h 头文件的修改。
这是译为用怎么回事呢?让我们一一道来。
简单的起作代码示例
一个头文件:hello.h
#ifndef _HELLO_ #define _HELLO_ #define NUM 1 #endif一个源文件:main.c
#include <stdio.h> #include "hello.h" int main(int argc, char *agv[]) { printf("NUM = %d \n", NUM); return 0; }Makefile 文件:
OBJS := main.o TARGET := main all : $(OBJS) gcc -o $(TARGET) $(OBJS) %.o: %.c gcc $< -c -o $@现在我们来第一次执行 make,编译一下:
$ make gcc main.c -c -o main.o gcc -o main main.o执行一下:
$ ./main NUM = 1我们现在把 hello.h 文件中的只修 NUM 改成 2,现在的头文文件修改时间是:
$ ll total 28 -rw-rw-r-- 1 root root 58 Jun 7 20:52 hello.h -rwxrwxr-x 1 root root 8608 Jun 7 20:51 main* -rw-rw-r-- 1 root root 122 Jun 7 20:51 main.c -rw-rw-r-- 1 root root 1528 Jun 7 20:51 main.o -rw-rw-r-- 1 root root 100 Jun 7 20:51 Makefile然后再执行 make 指令,编译一下:
$ make gcc -o main main.o可以看到:make 只执行了 Makefile 中的译为用链接指令(从目标文件 main.o 到可执行文件 main),并没有执行 gcc main.c -c -o main.o 这条编译指令来重新编译目标文件。
也就说明:make 并没有识别出 hello.h 这个头文件已经被改动了,亿华云尽管它“应该”可以从文件的修改时间上发现!
为什么会这样?
我们来看一下 Makefile 中的这个规则:
%.o: %.c gcc $< -c -o $@目标文件 main.o,只是依赖了 main.c 文件,并没有依赖 hello.h 文件。
make 的执行规则是:只有目标文件不存在,或者依赖文件比目标文件更新的时候,才会执行编译指令。
因此,虽然 hello.h 被修改了,但是它并不是目标文件 main.o 的依赖。
make 发现:main.o 在当前目录中是已经存在的,并且它比 main.c 更新,因此不会重新编译 main.o。
所以即使 hello.h 被修改了,也不会起作用,因为 make 压根就不把 hello.h 当做 main.o 的依赖!
注意:所有的操作过程没有执行 clean 操作。
最简单、无脑的方法
既然知道了原因,那就好办了,我们手动把头文件 hello.h 加到依赖中,云南idc服务商不就可以了吗?!
把 Makefile 中最后面几句修改成下面这样:
HEADERS := hello.h %.o: %.c ${ HEADERS} gcc $< -c -o $@也就是把 .h 文件,也加入到 .o 文件的依赖中,这样的话,每次修改 .h 文件后,再执行 make 指令时,就可以重新编译 .o 目标文件了。
您可试一下,这样做肯定是没有问题的。
到此,问题是被解决了,但是总觉得这样的方式比较粗鲁。
想一下:如果有很多的 .c 和 .h 文件呢,总不能手动一个一个添加吧?
高级一点的方法
修改 Makefile 为下面这样:
OBJS := main.o TARGET := main all : $(OBJS) gcc -o $(TARGET) $(OBJS) -include *.d %.o: %.c gcc $< -c -MMD -o $@改动部分有 2 处:
1. 添加了 -include *.d 指令;
2. gcc 编译指令中,添加了 -MMD 参数;
我们先执行一下试试。第一次编译:
$ ll // 查看当前文件 total 12 -rw-rw-r-- 1 root root 58 Jun 7 21:06 hello.h -rw-rw-r-- 1 root root 122 Jun 7 20:51 main.c -rw-rw-r-- 1 root root 119 Jun 7 21:05 Makefile $ $ make // 编译 gcc main.c -c -MMD -o main.o gcc -o main main.o $ $ ll // 再次查看当前文件 total 32 -rw-rw-r-- 1 root root 58 Jun 7 21:06 hello.h -rwxrwxr-x 1 root root 8608 Jun 7 21:06 main* -rw-rw-r-- 1 root root 122 Jun 7 20:51 main.c -rw-rw-r-- 1 root root 23 Jun 7 21:06 main.d -rw-rw-r-- 1 root root 1528 Jun 7 21:06 main.o -rw-rw-r-- 1 root root 119 Jun 7 21:05 Makefile $ $ ./main // 执行 NUM = 1有没发现:多出了一个文件 main.d,该文件内容是:
main.o: main.c hello.h这个文件正是因为 Makefile 中的 -MMD 这个参数导致生成的,而它的内容正是我们需要的云服务器提供商目标文件依赖信息。
然后在 Makefile 中,include 这个 .d 文件,从而让 make 知道:main.o 文件依赖于 main.c 和 hello.o 这 2 个文件。
这个时候,我们再来修改 hello.h 中的内容,例如:把 NUM 改成 10,再次编译、执行:
$ make gcc main.c -c -MMD -o main.o gcc -o main main.o $ $ ./main NUM = 10 本文转载自微信公众号「IOT物联网小镇」,可以通过以下二维码关注。转载本文请联系IOT物联网小镇公众号。很赞哦!(4791)
相关文章
- 公司和个人选域名方法一样吗?有什么不同?
- Python实现之激活函数
- 怎么把一个Java应用打包成Docker镜像
- 别被Vector最后一个元素Erase错误
- 小白注册网站域名该怎么办?有什么步骤?
- HarmonyOS非UI单元测试在DevEco Studio上的应用
- 你可能不知道,前端这五个有用的技术可以这么酷!
- 手把手教你搭建国产嵌入式模拟器SkyEye开发环境
- 众所周知,com域名拥有最大的流通市场和流通历史。最好选择com域名,特别是在购买域名时处理域名。其次可以是cn域名、net域名、org域名等主流域名,现在比较流行的王域名和顶级域名,都是值得注册和投资的。
- Stack Overflow 最新开发者调查报告:Rust 最受喜爱、PostgreSQL 最受欢迎