2011-10-26 93 views
3

我有一個C文件(說file1.c),調用函數 fun1(1,b)調用C函數沒有原型

此功能fun1(int a,int b)駐留在另一個C文件(比如file2.c)中,但其原型未包含在頭文件中(例如file2.h)。 file2.h包含在file1.c中。

我的問題是,如果我從file1.c調用fun1(a,b),它是否會通過將控制權交給file2.c中的函數定義來工作?或者會發生異常或預期的行爲是什麼?

我是否需要在file2.h中輸入fun1(int a, int b)的原型才能正常工作?

回答

5

有兩件事情可以根據不同的情況和你的編譯器發生:

  • 你得到一個編譯錯誤。編譯器拋出武器並拒絕產生一個目標文件。
  • 編譯器將函數聲明視爲調用隱含的函數聲明,並繼續進行鏈接。它可以假設你知道你在做什麼在函數參數方面。通常它也假定一個int返回類型。幾乎所有與之合作的編譯器都會在執行此操作時產生警告。
  • 編譯器將聲明視爲調用所隱含的聲明,但無法鏈接。像上面一樣,但是鏈接器會注意到你試圖調用的暗示函數和你實際編寫的函數是不同的並且死亡。

無論你應該提供一個原型。

+1

原型和頭文件夾雜宣言無關與生成的二進制文件的鏈接階段。任何C編譯器都會在最簡單的情況下拋出一個警告。在那裏你會得到一個警告的時間,比如使用gcc是,如果你有丟失的原型上的警告標誌,甚至然後它只是提高了全局函數和只爲實現該功能,並沒有正式存在的原型源文件。 ANSI C專門允許在沒有原型的情況下調用函數。 –

+1

@Ahmed我同意你的看法,它的沒有直接關係,但我的觀點是,通過不提供原型編譯器刪除檢查的一個水平。如果你與原型不匹配,編譯器會給你一個錯誤。如果你沒有原型,那麼它會落在鏈接器上來捕捉錯誤。你*希望*編譯器來捕捉錯誤,所以給它一個原型,以便它可以完成它的工作。 – bearda

3

預期的行爲是您的函數將被調用,只要目標文件鏈接在一起。任何未聲明的函數都被假定爲一個外部函數,該函數返回一個用於編譯目的的整數,以及一個用於鏈接目的的外部符號(指向對象文件)。我給你具體的例子:

foo.c的:

void foo(const char *name) { 
    printf("foo called with %s\n", name); 
} 

bar.c:

void bar(int a) { 
    printf("bar called with %d\n", a); 
} 

的main.c:

int main(int argc, char *argv[]) { 
    foo("Hello"); 
    bar(5); 
    return 0; 
} 

使用編譯的目標文件gcc:

gcc -fno-builtin -ansi -c -o foo.o foo.c 
gcc -fno-builtin -ansi -c -o bar.o bar.c 
gcc -fno-builtin -ansi -c -o main.o main.c 

這些不應產生任何警告或錯誤

現在它們關聯起來:

gcc -o progy main.o bar.o foo.o 

請注意,我用gcc的二進制文件鏈接,但那是等價的:

ld -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o progy /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.1/crtbegin.o main.o foo.o bar.o -lc -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.1 -lgcc -lgcc_s /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.1/crtend.o /usr/lib64/crtn.o 

在我的Linux 64位平臺上。不管是什麼您的構建目標平臺(GCC實際運行LD這樣做鏈接)

使用您的C編譯器(在我的情況,GCC)的鏈接將確保連接器調用正確。如果你使用IDE,那麼這些步驟都被一個漂亮的界面所隱藏。

+0

您的代碼示例導致C89不確定的行爲,因爲調用該函數沒有範圍原型導致返回類型爲'int'一個隱含的聲明;但是該函數實際返回'void'。在C99和C11中它是不合格的。 –