2012-04-23 42 views
1
// File foo1.c : 
#include <stdio.h> // once 
void foo1(void); 
void foo1(void){ 
    puts("foo1"); 
} 

// File foo2.c : 
#include <stdio.h> // again 
void foo2(void); 
void foo2(void){ 
    puts("foo2"); 
} 

// File foomain.c : 
#include <stdio.h> // yet again 
void foo1(void); // again 
void foo2(void); // again 
int main(void){ 
    foo1(); 
    foo2(); 
    puts("foomain"); 
    return 0; 
} 

// create object files 
gcc -fPIC foo1.c -o foo1.o // 1 stdio.h 
gcc -fPIC foo2.c -o foo2.o // 1 stdio.h 

// create shared library 
gcc -fPIC -shared foo1.o foo2.o -o foo.so // foo.so contains stdio.h 2 times ? 

// build entire program 
gcc foo.so foomain.c -o foomain // foomain contains 1 stdio.h plus the 2 from foo.so ? 
  1. 爲什麼整個程序包含3 stdio.h中?似乎多餘,爲什麼不只是1?編譯器不應該只需要1?整個源文件中都有重複的頭文件?

  2. 它是有道理的對象文件包含原型,但爲什麼他們必須在foomain.c中再次指定?編譯器不知道它們已經在foo.so中指定了嗎?

回答

5

這是因爲每個文件都是單獨編譯的,所以每次編譯器都應該知道用於執行編譯時檢查的所有函數的簽名。因此,每個文件都必須包含所有使用的聲明,這些聲明在編譯文件之前由預處理器包含在內。

1

如果你看看大多數的頭文件上他們有包括警衛阻止雙重包容。

#ifndef FOO 
#define FOO 

#endif 

更多信息請參見Include Guard

+0

但是這是基於每個文件的文件,它不適用於其他文件,對不對? gcc帶有適當的警告標誌,如果有重複的話,會輸出警告(錯誤?)。 – 2012-04-23 14:53:23

0

您可以像@Jeff建議的那樣使用include guard,或者將#pragma once放在每個標題的頂部。

+0

更好地使用'#ifdef'標頭守衛作爲'#pragma'指令執行實現定義的動作。 – ouah 2012-04-23 10:13:25

1

#include行實際上並不是編譯器的一部分,而是C preprocessor

預處理程序所做的與#include線的是,真正包含文件到源代碼,並創建包含與#include線由文件中的內容替換文件的內容被納入新的臨時文件。

根本不需要包含文件,如果你只是在調用函數。您可能會收到有關未聲明功能的警告,但可以自行添加這些功能的原型。例如,在你的主源文件,你只用puts,而不是包括<stdio.h>您可以添加一個原型是這樣的:

int puts(const char *s); 

然而,<stdio.h>還定義了一些結構(如FILE結構),並聲明瞭一些變量(如stdout),如果您使用其中任何一種,您也需要頭文件。