首页 > 其他分享 >Makefile-只修改了.h头文件,编译为什么不起作用

Makefile-只修改了.h头文件,编译为什么不起作用

时间:2024-07-17 20:52:10浏览次数:14  
标签:gcc rw 头文件 不起作用 make Makefile main root hello

Makefile-只修改了.h头文件,编译为什么不起作用?-腾讯云开发者社区-腾讯云 (tencent.com)

 

不知道各位小伙伴是否碰到过这样的情况:

一个 .c 文件 include 另一个 .h 头文件,使用 Makefile 来构建(编译)应用程序。

第一次编译、执行,很正常!

但是此时,如果修改了 .h 头文件,再次编译时,就出现问题了:

预期的执行流程是:make 发现 .h 头文件的修改时间更新,于是重新编译包含这个头文件的所有 .c 文件。

可实际的结果却是:make 并没有识别出 .h 头文件的修改。

这是怎么回事呢?让我们一一道来。

简单的代码示例

一个头文件:hello.h

代码语言:javascript复制
#ifndef _HELLO_
#define _HELLO_

define NUM 1

endif

一个源文件:main.c

代码语言:javascript复制
#include <stdio.h>

include "hello.h"

int main(int argc, char *agv[])
{
printf("NUM = %d \n", NUM);
return 0;
}

Makefile 文件:

代码语言:javascript复制
OBJS := main.o
TARGET := main

all : $(OBJS)
gcc -o $(TARGET) $(OBJS)

%.o: %.c
gcc $< -c -o $@

现在我们来第一次执行 make,编译一下:

代码语言:javascript复制
$ make
gcc main.c -c -o main.o
gcc -o main main.o

执行一下:

代码语言:javascript复制
$ ./main
NUM = 1

我们现在把 hello.h 文件中的 NUM 改成 2,现在的文件修改时间是:

代码语言:javascript复制
$ 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 指令,编译一下:

代码语言:javascript复制
$ make
gcc -o main main.o

可以看到:make 只执行了 Makefile 中的链接指令(从目标文件 main.o 到可执行文件 main),并没有执行 gcc main.c -c -o main.o 这条编译指令来重新编译目标文件。

也就说明:make 并没有识别出 hello.h 这个头文件已经被改动了,尽管它“应该”可以从文件的修改时间上发现!

为什么会这样?

我们来看一下 Makefile 中的这个规则:

代码语言:javascript复制
%.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 加到依赖中,不就可以了吗?!

Makefile 中最后面几句修改成下面这样:

代码语言:javascript复制
HEADERS := hello.h
%.o: %.c ${HEADERS}
	gcc $< -c -o $@

也就是把 .h 文件,也加入到 .o 文件的依赖中,这样的话,每次修改 .h 文件后,再执行 make 指令时,就可以重新编译 .o 目标文件了。

您可试一下,这样做肯定是没有问题的。

到此,问题是被解决了,但是总觉得这样的方式比较粗鲁。

想一下:如果有很多的 .c.h 文件呢,总不能手动一个一个添加吧?

高级一点的方法

修改 Makefile 为下面这样:

代码语言:javascript复制
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 参数;

我们先执行一下试试。第一次编译:

代码语言:javascript复制
$ 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,该文件内容是:

代码语言:javascript复制
main.o: main.c hello.h

这个文件正是因为 Makefile 中的 -MMD 这个参数导致生成的,而它的内容正是我们需要的目标文件依赖信息。

然后在 Makefile 中,include 这个 .d 文件,从而让 make 知道:main.o 文件依赖于 main.c 和 hello.o 这 2 个文件。

这个时候,我们再来修改 hello.h 中的内容,例如:把 NUM 改成 10,再次编译、执行:

代码语言:javascript复制
$ make
gcc main.c -c -MMD -o main.o
gcc -o main main.o
$
$ ./main
NUM = 10

Bingo,结果正确!

------ End ------

标签:gcc,rw,头文件,不起作用,make,Makefile,main,root,hello
From: https://www.cnblogs.com/peifx/p/18308258

相关文章

  • 条件断点 预编译头文件
    作用:当满足某些条件时才会触发断点怎么设置:代码左侧单击添加断点,右键条件,添加触发条件;就是说在条件断点处暂停运行;预编译头文件作用:抓取头文件,编译成二进制存放在文件中,可以避免多个文件包含相同头文件时多次编译读头文件注意:不能将经常改动的头文件放到预编译头文件中会......
  • CCS工程导入后编译时无法读取头文件的问题
    CCS工程导入后编译时无法读取头文件的问题,主要是由移植之后的文件搜索路径所指向的文件夹不存在导致的。有两种解决办法:①导入工程时将所需要的所有.h、.c以及.lib文件一并打包在工程文件中,复制到Workspace并重新选取搜索路径;②导入工程的电脑安装有C2000WARE,修改CCS中相对路径......
  • 函数,调试,循环,头文件
    1.关于函数作用:避免代码重复可以方便维护,因为当多个地方需要用到同样的代码时,如果要做出修改只需要改函数就能整体修改。函数通常都会有返回值,什么类型的函数返回什么样的类型的值函数通常分为函数声明和函数参数通常声明放在头文件中函数定义发在C++文件中这样做可以在别的......
  • c++所有头文件.h
    代码:(包括c++12的非标准头文件)#ifndef_EW_H_#define_EW_H_#include<tchar.h>#include<synchapi.h>#include<algorithm>#include<bitset>#include<cctype>#include<cerrno>#include<clocale>#include<cmath>#inc......
  • makefile for循环 /bin/sh: -c: line 3: syntax error: unexpected end of file
    1.错误makefile示例,有syntaxerror:unexpectedendoffilefo=onethreefivetest:forjin$(fo);do\echo$$j;\end在写for循环的时候,注意格式for之前用tab键do前后有空格在命令行中输入maketest运行结果如下所示2.修改了makefile,没有error......
  • Makefile学习笔记
    上述代码中一共有5条规则,1-2行为第一条规则,3-4行为第二条规则,5~6行为第三条规则,7-8行为第四条规则,10~12为第五条规则,make命令在执行这个Makefile的时候其执行步骤如下:第一条规则:main是我们想要的可执行文件,通过main.o、input.o和calcu.o这三个文件......
  • 点击事件不生效选择不到,元素被遮挡点击不起作用
    解决方案:两种方案:假设:外层遮挡的类名为:outer,被遮挡的类名为:Inner。1:在不破坏原有样式的基础上增加position:relative;然后z-index控制谁在上面即可.outer{ position:relative;z-index:1;}.Inner{ position:relative;z-index:2;}2.如果点击事件还不......
  • 【C语言小知识】备选拼写:iso646.h头文件
    备选拼写:iso646.h头文件C语言中有3种逻辑运算符:逻辑运算符含义&&与||或!非假设exp1和exp2是俩个简单的关系表达式,那么当且仅当exp1和exp2都为真时,exp1&&exp2才为真。如果exp1或exp2为真,则exp1||exp2为真。如果exp1为假,则!exp为真;如果exp1为真,则!exp为假。i......
  • 关于头文件包含这件事(新手教程,神犇勿进)
    一、如何包含头文件?#include<头文件名>在头文件名这里写入头文件就好了。二、万能头文件是什么?万能头文件的样子长这样:bits/stdc++.h顾名思义,万能头文件就是你包含了这个头文件之后就不用包含其他头文件,例如,在以下程序中:#include<iostream>#include<queue>#include<st......
  • C++11 标准库头文件模拟实现
    系列文章目录文章目录系列文章目录前言●智能指针模板●Vector1.简单版本2.X总结前言暂不考虑支持多线程常用STL的简单实现,主要内容百行左右完成,意在理解STL的原理●智能指针模板SharedPtr#include<assert.h>#include<atomic>template<classT......