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$


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM