2010-12-03 52 views
20

我是C的初學者,我需要學習makefile的工作原理,並且對C文件的組合工作方式有點困惑。假設我們有一個main.c,一個foo.c和一個bar.c.應該如何編寫代碼以便main.c能夠識別其他文件中的函數?另外,在foo.c和bar.c中,所有代碼都寫在主函數中,還是需要編寫其他函數來滿足我們需要的功能?我已經閱讀了關於如何編寫makefile的教程,而且大部分都是有意義的,但我仍然對它的基本後勤有些困惑。C-中的多個源文件makefile的工作原理如何?

+1

剛一說明,而不是一個完整的答案是:你其實並不想在多個文件中的'main`功能被編譯到同一個目標,然後爲你將有兩個同名的函數。 – 2010-12-03 20:33:57

回答

24

通常會發生什麼是您將定義您的函數的頭文件中的其他文件,然後可以包含在main.c中。例如,考慮這些內容摘要:

的main.c:

#include "foo.h" 

int main(int argc, char *argv[]) { 
    do_foo(); 
    return 0; 
} 

了foo.h:

void do_foo(); 

foo.c的:

#include <stdio.h> 
#include "foo.h" 

void do_foo() { 
    printf("foo was done\n"); 
} 

會發生什麼是主.c將變成一個目標文件(main.o),並且foo.c將變成一個目標文件(foo.o)。然後鏈接器將這兩個文件鏈接在一起,這就是main.c中的do_foo()函數與foo.o中的函數「關聯」的地方。

例GCC命令: GCC -o myprogram main.c中的foo.c

實施例生成文件

myprogam: main.o foo.o 
    gcc -o myprogram main.o foo.o 

main.o: main.c foo.h 
    gcc -c main.c 

foo.o: foo.c foo.h 
    gcc -c foo.c 
2

需要從外部調用foo.c的函數foo.c應該在foo.h中有原型。需要調用這些函數的外部文件應爲#include "foo.h"。如果它們是與main.c相同的程序的一部分,則foo.cbar.c甚至不應具有main()函數。

Makefiles定義目標。對於簡單的程序,你可以只有一個編譯整個事物的目標。更復雜的(閱讀:更大的)程序可以有中間目標(如foo.o),這將允許make避免不必要的重新編譯。 make確定給定目標是否需要重新編譯,它檢查所有先決條件(冒號後面的內容)的修改時間,並且如果它們中的任何一個在目標文件本身的最後更改時間之後出現,它得到重建。

這裏是一個非常簡單的例子:

的main.c:

#include "foo.h" 

int main() 
{ 
    fooprint(12); 
    return 0; 
} 

foo.c的:

#include "stdio.h" 
#include "foo.h" 

void fooprint(int val) 
{ 
    printf("A value: %d\n", val); 
} 

了foo.h:

void fooprint(int val); 

的Makefile:

main: main.c foo.o 
    gcc -o main main.c foo.o 

foo.o: foo.c 
    gcc -c foo.c 

然後你可以運行make main它將編譯foo.cfoo.o然後編譯main.c中,並與foo.o.掛靠如果修改main.c,則只需重新編譯main.c並將其與已建好的foo.o鏈接。

+0

如果你的第一句話是「foo.c中的函數需要從外部調用foo.c應該在foo.h中有原型」,那麼省略「do not」並因此反轉你實際說的內容?推論是(1)不需要從外部調用的函數應該被定義爲靜態函數,(2)並行地,需要從bar.c外部調用的bar.c中的函數應該被定義爲靜態函數在bar.h中有原型(bar.c中的函數不應該從bar.c外部調用,應該定義爲靜態函數)。 – 2010-12-04 16:37:40

+0

@Jonathan:呃,是的。我正要提出關於「靜態」的觀點,但是決定反對它。 – nmichaels 2010-12-04 20:41:01

13

關於C/C++編譯

當有一組你通常做2件事:

  • 將每個源文件(.c)編譯爲目標文件(.o)
  • 將所有目標文件鏈接到可執行文件中。

源文件是獨立 - 你需要頭文件能夠提供「信息」(聲明)有關給定模塊中的功能是爲了讓任何其他模塊中使用它們。頭文件不是自己編譯的 - 它們是#include d作爲源文件的一部分。

看看下面的命令如何以及make如何處理這些命令。


關於makefile文件

Makefile文件是一組目標和規則來構建它們。 A 目標是「可以構建並在給定文件中生成結果的東西」。 (也存在「僞造」目標,它們不會生成文件,只是在那裏執行命令 - 常見的命令是clean以刪除編譯結果)。

每個目標有2個部分組成:

  • 依賴性的列表,「靈敏度列表」(其它所需要此目標文件和目標):後,用逗號分隔),
  • shell命令列表它們是爲了構建這個目標而被執行的(下面是上面的縮進)

考慮這個例子:

main: main.o module1.o module2.o 
    g++ main.o module1.o module2.o -o main 

這意味着:「打造文件main,我需要首先確保目標main.omodule1.omodule2.o都是最新的;那麼我需要調用以下命令......「。

這也可以被改寫爲:

main: main.o module1.o module2.o 
    gcc $^ -o [email protected] 

的變量(一切開始$是一個變量)將擴大到依賴關係列表和目標名稱,如您所願。

您可以定義自己的變量和擴大如下:

OBJS = main.o module1.o module2.o 

main: $(OBJS) 
    # code goes here 

您編譯個人翻譯單元如下:

main.o: main.c 
    gcc -c $< -o [email protected] 
    # note the -c option, which means: "compile, but don't link" 
    # $< will expand to the first source file 

您可以添加標題依賴重建當main.c或其任何頭文件發生更改時,main.o:

main.o: main.c module1.h module2.h 
    gcc -c $< -o [email protected] 

爲了不超過寫同樣的命令一遍又一遍,你可以定義一個一般的規則,只是供應的依賴關係(如果你想):

%.o: %.c 
    gcc -c $< -o [email protected] 

main.o: main.c module1.h module2.h 
module1.o: module1.c module1.h module2.h 

還有一些神奇的生成dependencies automatically (see link) 。使用Make的一個缺點是它並不是自己做的(就像一些構建系統一樣 - 比如我更喜歡C/C++的SCons)。

1

與C程序的結構沒有多大關係。所有make都會定義一個依賴關係樹,並在發現依賴關係不在重擊時執行命令。我的說法,在一個makefile:

foo.exe : foo.c bar.c baz.c 

只是經濟特區:foo.exe的是依賴於foo.c的,bar.c和baz.c.這心腹vocce,得到擴展,使用make的默認規則集,喜歡的東西:

foo.exe : foo.obj bar.obj baz.obj 

foo.obj : foo.c 

bar.obj : bar.c 

baz.obj : baz.c 

製作簡單地走依賴關係樹開始在它的根目錄(在這種情況下,foo.exe的)。如果目標不存在或者其所依賴的目標之一比目標更新,則執行相關的命令。使依賴關係正確。

請參閱Managing Projects with Make來自O'Reilly的超過您可能想知道的。

就問題的第二部分而言,答案只有兩個字母:K and R。他們的The C Programming Language可以說是有史以來最好的計算機編程書籍之一。

alt text

2

本質makefile文件包含的形式的規則:

<this file> : <needs these files> 
    <and is created by this command> 

通常必須至少一個高層次的目標,如果它的任何依賴關係不存在,請查找一個規則將該文件作爲目標。它在遞歸執行頂層目標之前已經解決了頂級目標的所有依賴關係(如果有的話 - 依賴項和命令都是規則中的可選字段)

make文件可以有基於模式的'默認規則',並且有各種文件匹配場景的內置宏以及用戶定義的宏和包含嵌套的makefile。

我簡化了上面的規則表單到最常見的情況。實際上,該命令根本不需要創建目標,它只是一旦依賴項中的所有文件都存在就執行的命令。而且目標也不一定是文件。通常頂級目標是稱爲「全部」或類似的「虛擬」目標。

當然有許多微妙之處和細微差別,詳細請見the manual(GNU make特別指出,還有其他make工具)。

1

Makefile如何工作?

=>當在終端上執行make命令時,它會在當前目錄中查找名爲makefile或Makefile的文件並構造一個依賴關係樹。

如果你有幾個Makefile文件,然後你可以使用以下命令執行特定的:

      make -f MyMakefile 

=>基於在makefile中指定的make目標,如果存在這一目標的相關文件進行覈對。如果它們存在,它們是否比目標本身更新,通過比較文件時間戳。

Here our first and default target is 「all」 which looks for main.o and function.o file dependencies. Second and third target is main.o and function.o respectively which have dependencies of main.c and function.c respectively. 

=>執行相應的目標的指令之前,它的依賴必須滿足,如果不滿足他們,這些依賴的目標是在給定的make目標前執行,以提供缺少的依賴。

=>當目標是文件名時,make比較目標文件及其相關文件的時間戳。如果依賴文件比目標文件新,則目標執行否則不執行。

In our case, when first target 「all」 start executing it looks for main.o file dependency, if its not met. Then it goes to second target main.o which check for its dependency main.c and compare time-stamp with it. If target found main.c dependency is updated, then target execute else not. Same process is follow for next target function.o. 

=>因此,它遞歸地檢查依賴關係樹中的所有方法,到源代碼文件。通過這個過程,通過僅執行需要執行的命令,節省時間,基於哪些源文件(列爲依賴項)已被更新,並且具有比其目標更新的時間戳。

=>現在,當目標不是文件名(我們稱之爲「特殊目標」)時,顯然不能比較時間戳來檢查目標的依賴關係是否更新。因此,這樣的目標總是被執行。

In our Makefile, special targets are 「all」 and 「clean」. As we discussed target 「all」 earlier, but we not discuss target clean. Target clean removes the all object files created during compilation and binary executable files according to command. 

爲了執行每個目標,在執行它們時打印操作。請注意,由於安全執行,每個命令都在單獨的子shell環境中執行,因此它們無法更改可能影響其他目標執行的當前shell環境。例如,如果一個命令包含cd newdir,當前目錄將僅針對該行命令進行更改,對於下一行命令,當前目錄將保持不變。

來源: - http://www.firmcodes.com/linux/write-first-makefile-c-source-code-linux-tutorial/