Makefile中头文件在依赖关系中作用
摘于:http://bbs.csdn.net/topics/120024677
(1)在makefile的依赖关系中用不用体现.h头文件?
(2)如果在依赖关系中要体现.h头文件,应该体现到什么层次?
==============================
(1)在makefile的依赖关系中用不用体现.h头文件?
==============================
下面是我的一些认识:
头文件中定义的是接口(函数接口,文件外全局变量和宏定义),它的作用是向调用文件封装函数的实现过程。在第一次make的时候依赖关系中没有.h文件是没有关系的。所以主要讨论在修改文件的时候重新make的情况。
(一)接口
以一个类CBase的.h文件和.cpp文件为例:
//CBase.h
Class CBase
{
....
};
//CBase.cpp
CBasee::CBase(){}
....
//file upper.cpp
#include “CBase.h”
...
按照书上的例子这样的文件依赖关系应该为:
upper.o : upper.cpp CBase.h
gcc ........
CBase.o : CBase.cpp CBase.h
gcc ........
(1)如果函数实现(CBase.cpp)被修改而函数接口(CBase.h)没有被修改。这种情况应该是出现最多的情况,我们需要对函数的实现进行修改。这个时候很明显CBase.cpp将会有一个新的时间戳。所以在依赖关系中有没有CBase.h都可以。
(2)函数接口被修改。也就是.h文件被修改。在所有正常情况下对接口(.h)的修改必将导致相应的实现(.cpp)的修改和调用文件(upper.cpp)的修改(如果调用文件中使用了.h中的接口,淡然没使用upper.o就更不需要重新编译)。这样CBase.cpp和upper.cpp都有一个新的时间戳,所以在依赖关系中更不需要.h头文件。
(二)文件外全局变量。在头文件中的全局变量的申明是通知使用者在连接的时候要到文件中去找定义。所以在头文件中修改全局变量没有任何意义。一种情况是
//a.cpp
int a =5;
...
//a.h
extern int a;
...
//b.cpp
...
a++;
...
之前的make是ok的。如果我们人为的将a.h的全局变量的声明改为
extern int amm ;
这样make以后是会报错的,除非在依赖关系中体现.h头文件。
(三)宏定义
我想这个是要使用.h头文件的最重要因素了。如果我们在头文件中定义
#define PI 3.14
然后我们想提升pi的精度,修改头文件
#define PI 3.1415
这样我们必须将头文件包含在makefile的依赖关系中。
===================================
(2)如果在依赖关系中要体现.h头文件,应该体现到什么层次?
===================================
一个例子
在一个Has_a的类关系中,
class CWhole
{
private:
CPart1 Part1;
CPart2 Part2;
...
}
这样我们在CWhole类的头文件中显然要include
#include "CPart1.h"
#include "CPart2.h"
....
而在CWhole类的实现文件中只要include
#include "CWhole.h"
那么我们在CWhole.cpp的依赖关系中要怎么写?
(1)CWhole.o :CWhole.cpp CWhole.h
gcc ...
(2) CWhole.o : CWhole.cpp CWhole.h CPart1.h CPart2.h ...CPartn.h
gcc ...
(3) ?????万一CPart1.h中#include的头文件要不要写在CWhole.o的依赖关系中?下面所有的头文件要不要写在依赖关系中
头大,请大家帮我想想
---------------------------------------------------------------------------------------------------------------
在 C++ 中因为头文件包含了一些实现的内容(比如类成员的定义),而这些内容由于面向对象的封装原则,客户代码是不去理会的,然而编译客户代码时却是需要的。那么当这些内容发生改变的时候,实现文件(.cpp)往往也发生改变,这导致相应 .o 的重新编译生成。然而如果不把该头文件加入到客户代码的依赖关系中,因为客户代码没有改变,时间戳未发生变化,客户代码不会被重编译,而这种情况在连接时也未必会被发现,最后只能导致难以调试的运行时错误。
例如:
class SomeLinkList {
public:
...
bool HasDupElement(); // 是否存在重复的元素
...
private:
Node * pHead;
Node * pTail;
};
HasDupElement() 这个方法原先以每次遍历链表的方式实现,后来程序员想到在每次插入新元素的时候查找一下用一个成员变量保存结果,每次返回这个变量值就可以了,效率更高。于是他修改了实现,并在类定义中添加了一个 bool 型的私有成员。这个改动对客户代码没有影响,客户代码不需要修改,这正是面向对象希望的。然而 C++ 的封装并不完全,在生成机器代码时客户代码依赖于使用的类的大小,因此虽然没有修改客户代码,但客户代码仍需要被重新编译。这时客户代码的时间戳不能解决问题,必须依赖于这个头文件的时间戳。
因此我认为不管何时,把用到的可能修改的头文件加入到依赖关系中可以省去很多麻烦,而这也恰恰反应了实际情况,也让自己对代码的依赖关系更加清晰,也让自己在代码中包含头文件时更加谨慎(仅包含需要的,这也会减少编译时间,减少包含关系错综复杂带来的混乱)。那种可以不写头文件依赖的技巧带来的结果是得不偿失的。
----------------------------------------------------------------------------------------------------------------------
用 gcc -M 选项吧
----------------------------------------------------------------------------------------------------------------------
如果用GNU make,就会变得很简单了,可以参考下面的Makefile:
mymtom@fc6:src/csdn/make$ cat Makefile
PROG = hello
SRCS = main.c hello.c
DEPS = $(SRCS:.c=.d)
OBJS = $(SRCS:.c=.o)
RM = rm -f
.SUFFIXES: .d
.c.d:
$(CC) -MM $(CPPFLAGS) $< > $@
all: $(PROG)
dep: $(DEPS)
hello: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
-include $(DEPS)
clean:
$(RM) $(OBJS) $(PROG) $(DEPS)
mymtom@fc6:src/csdn/make$ make
cc -MM hello.c > hello.d
cc -MM main.c > main.d
cc -c -o main.o main.c
cc -c -o hello.o hello.c
cc main.o hello.o -o hello
mymtom@fc6:src/csdn/make$ cat main.c
#include <stdio.h>
#include "hello.h"
int main(void)
{
hello();
return 0;
}
mymtom@fc6:src/csdn/make$ cat hello.c
#include <stdio.h>
#include "hello.h"
void hello(void)
{
(void)printf("%s\n", HELLO);
}
mymtom@fc6:src/csdn/make$ cat hello.h
#define HELLO "Hello!"
void hello(void);
mymtom@fc6:src/csdn/make$
Makefile中头文件在依赖关系中作用的更多相关文章
- makefile 自动处理头文件的依赖关系 (zz)
现在我们的Makefile写成这样: all: main main: main.o stack.o maze.ogcc $^ -o $@ main.o: main.h stack.h maze.hst ...
- Makefile目标,伪目标,头文件自动依赖
目标 即我们最终要生成的文件,make默认生成第一个目标,注意 makefile中tab和空格不是一回事,规则使用tab缩进,编辑器不要设置诸如"将tab替换为空格之类的选项",目 ...
- c++中头文件与实现文件的关系
转自:http://xiangyanglai.blog.163.com/blog/static/2047252022012715103338279/ 关于两者以前的关系,要从N年以前说起了~ long ...
- C++中头文件与源文件的作用详解
一.C++ 编译模式 通常,在一个 C++ 程序中,只包含两类文件―― .cpp 文件和 .h 文件.其中,.cpp 文件被称作 C++ 源文件,里面放的都是 C++ 的源代码:而 .h 文件则被称作 ...
- Makefile 9——为依赖关系文件建立依赖关系
现在我们再对complicated项目做一些更改,增加程序文件间依赖关系的复杂度. /× main.c ×/ #include"foo.h" int main(void) { fo ...
- 在源文件(.c)和头文件(.h)中声明和定义的区别——C语言
最近在看多文件编程的时候遇到的一个问题,本来以为理解了声明和定义的区别(然而并没有····),也算是重新认识了一次声明和定义,下面上代码 情形一:在源文件(.c)中 相信大部分读者对声明和定义的理解是 ...
- C/C++ 中头文件相互包含引发的问题
转自:http://blog.csdn.net/hazir/article/details/38600419 今天下午遇到一个头文件相互包含而导致的编译问题,花了我不少时间去调试没找到问题,最后晚上跟 ...
- Visual Studio中头文件stdafx.h的作用
在较新版的Visual Studio中,新生成的C++项目文件的的头文件夹下会默认有头文件stdafx.h,而源文件夹下则默认有源文件stdafx.cpp,手动将这些文件删除后,编译时系统还会报错.下 ...
- c中头文件在cpp文件里引用和.h文件引用的思考
我们在编敲代码中头文件是常常使用的. 可是头文件是应该包括在.H文件里还是在.cpp文件里.在这个其中有什么样去差别呢. 假如说我们编写了一个a.cpp .我们将a.cpp文件的变量和函数申明在a. ...
随机推荐
- Leetcode: Heaters
Winter is coming! Your first job during the contest is to design a standard heater with fixed warm r ...
- Linux字符界面安装VMware tools
以往用VMware虚拟机都是装的桌面版,无奈实验室电脑属于老爷机,跑桌面linux实在有点吃不消,只能装个Basic Server玩玩了... 在桌面环境下装VMwaretools很简单,直接点击VM ...
- asp.net5 操作Cookie
写入Cookie var CookieOption = new CookieOptions(); CookieOption.Path = "/"; //on localhost, ...
- 诺基亚远去,《惊奇UCD》带你重塑用户体验
我所说的成功的用户体验,是指我见过或听说过大量的用户非常喜爱我为手机行业做出的那些贡献.我的职业幸福感并不取决于我的经理或CEO说了什么,而是取决于我从实际用户那里听到了什么. ...
- next([expr]) 取得一个包含匹配的元素集合中每一个元素紧邻的后面同辈元素的元素集合。
描述: 找到每个段落的后面紧邻的同辈元素. HTML 代码: <p>Hello</p><p>Hello Again</p><div>< ...
- Java8-Function使用及Groovy闭包的代码示例
导航 定位 概述 代码示例 Java-Function Groovy闭包 定位 本文适用于想要了解Java8 Function接口编程及闭包表达式的筒鞋. 概述 在实际开发中,常常遇到使用模板模式的场 ...
- 161228、Java IO流读写文件的几个注意点
平时写IO相关代码机会挺少的,但却都知道使用BufferedXXXX来读写效率高,没想到里面还有这么多陷阱,这两天突然被其中一个陷阱折腾一下:读一个文件,然后写到另外一个文件,前后两个文件居然不一样? ...
- angular 后台交换实例
<!DOCTYPE html><html lang="en" ng-app="myApp"><head> <meta ...
- 处理session跨域几种的方案
常用跨域共用session的是登录模块,我相信很多开发的朋友的都遇到过,只需要一个地方登录,相关联的网站也是处于登录状态.两种情况:一种9streets.cn和a.9streets.cn之间,另一种是 ...
- Oracle instr
instr函数 instr函数在Oracle/PLSQL中是返回要截取的字符串在源字符串中的位置.instr是一个非常好用的字符串处理函数,几乎所有的字符串分隔都用到此函数. 作 用 返回 ...