2013-04-18 23 views
5

用下面的代碼:關於C函數原型和編修問題

int main(){ 
    printf("%f\n",multiply(2)); 
    return 0; 
} 

float multiply(float n){ 
    return n * 2; 
} 

當我嘗試編譯,我得到一個警告:「‘%F’預計‘雙’,但參數的類型爲‘廉政’」和兩個錯誤:「相乘類型」,「以前的'乘法'隱式聲明在這裏。」

問題1:我猜測,這是因爲,由於編譯器沒有的功能「乘法」,當他遇到它的第一次,他將創造一個原型知識,併發明原型總是假定「詮釋」返回並作爲參數。所以本發明的原型將是「int multiply(int)」,因此也是錯誤。它是否正確?

現在,以前的代碼甚至不會編譯。但是,如果我在兩個文件中打破了這樣的代碼:

#file1.c 
int main(){ 
    printf("%f\n",multiply(2)); 
    return 0; 
} 

#file2.c 
float multiply(float n){ 
    return n * 2; 
} 

,並執行「GCC在file1.c file2.c中-o文件」,它還是會給予一次警告(即printf的期待雙,但越來越INT )但錯誤不會再顯示出來並且會被編譯。

問題2:當我把代碼分解成2個文件時,怎麼會編譯它?

問題3:一旦我運行上面的程序(版本分成2個文件),結果是0.0000被打印在屏幕上。怎麼來的?我猜測編譯器再次發明了一個與函數不匹配的原型,但爲什麼打印0?如果我將printf(「%f」)更改爲printf(「%d」),它會打印出1.再次說明幕後發生的事情?

非常感謝。

回答

4

於是發明了原型將被視爲「整數乘法(INT)」,因此錯誤。它是否正確?

絕對。這是爲了與缺乏函數原型的pre-ANSI C向後兼容,並且沒有類型聲明的所有內容都是隱式的int。編譯器編譯你的main,創建一個隱含的定義int multiply(int),但是當它找到真正的定義時,它會發現謊言,並告訴你它。

如何將代碼分解成2個文件編譯?

編譯器從未發現了謊言的原型,因爲它編譯一個文件上的時間:它假定multiply需要一個int,並返回你的mainint,並在multiply.c沒有發現任何矛盾之處。但是運行這個程序會產生未定義的行爲。

一旦我運行上面的程序(版本分成2個文件),結果是0.0000被打印在屏幕上。

這是上述未定義行爲的結果。該程序將編譯和鏈接,但因爲編譯器認爲multiply需要int,它永遠不會將2轉換爲2.0F,而multiply永遠不會找出。同樣,在您的multiply函數中,通過將int重新解釋爲作爲float計算的錯誤值將再次被視爲int

+0

有道理。唯一的一點仍不清楚,爲什麼編譯器不會找到與2個文件的謊言/矛盾。畢竟它仍然會在通過main.c運行時創建「int multiply(int)」原型,一旦它到達第二個文件,它將遇到「float multiply(float)」函數聲明,這明顯與先前的原型。 –

+2

@DanielS好點!我編輯了答案,提到了爲什麼:本質上,編譯器一次編譯每個文件,每次編譯一個源文件。這不同於Java,它一次考慮所有文件。使用C和C++,每個翻譯單元(這是'.c'文件的花式名稱)被單獨編譯,然後鏈接器將結果彙總在一起。然而,當連接器進入遊戲時,所有的類型信息都消失了:連接器的字節,偏移量和地址水平低得多,因此它們也無法檢測到差異。 – dasblinkenlight

+1

@DanielS:編譯器一次處理一個文件;在處理'file1.c'時,它對file2.c的內容一無所知,反之亦然。在構建'file1.c'時,它只知道'multiply'在使用前沒有被聲明。在構建'file2.c'時,它不知道'multiply'正在另一個翻譯單元中使用,沒有在範圍內進行正確的聲明。 –

1

問題1:是的你是對的。如果沒有函數原型,默認類型爲int

問題2:當你編譯這段代碼爲一個文件,編譯器發現已經存在的功能命名multiply,它有不同的類型應該( double而不是int)。因此編譯不起作用。

當您將其分成兩個文件時,編譯器會生成兩個.o文件。在第一個中,它假設multiply()函數將在其他文件中。然後鏈接器將這兩個文件鏈接到一個二進制文件中,並根據名稱multiplyfloat multiply()的呼叫插入到編譯器在第一個.o文件中假定的int multiply()處。

問題3:如果你讀int2作爲float,你會得到一個非常小的數目(〜1/2^25),所以以後你乘以2它,它仍然保持了格式太小%f。這就是爲什麼你看到0.00000

1

未指定的函數的返回類型爲int(這就是爲什麼您會收到警告,編譯器認爲它會返回整數)以及未知數量的未指定參數。

如果你分解你的項目在多個文件中,只需聲明一個函數原型,然後再調用其他文件中的函數,並且所有函數都可以正常工作。

1

問題1:

所以發明的原型是 「整數乘法(INT)」,因此 錯誤。它是否正確?

不exactelly是因爲它取決於你的CX(C89,C90,C99,...)

爲函數的返回值,之前C99是明確SPECI網絡主編,如果沒有函數聲明爲可見譯者提供了一個。這些隱式聲明默認爲INT

理由的返回類型從C Standard(6.2.5頁506)

此前C90沒有函數原型。開發人員預計 能夠交換已簽名和未簽名 版本的相同整數類型的論據。如果參數類型 具有不同的符號性,那麼必須拋出一個參數, 被認爲是與C的易於類型檢查系統和 小侵入性相反的。原型的引入並沒有完全消除參數互換性的問題。省略號 表示沒有任何關於1590省略號的已知信息 不提供任何信息預期類型的​​參數。類似地,對於 函數返回值,在C99之前,明確指定 如果沒有函數聲明可見,則翻譯器提供一個函數聲明。 這些隱式聲明默認爲int的返回類型。如果 實際函數發生返回unsigned int類型,那麼這樣的 默認聲明可能會返回意外的結果。很多 開發人員對功能聲明有着隨意的態度。 我們其他人不得不忍受委員會的後果,而不是 想要打破他們寫的所有源代碼。該 互換性函數的返回值現在是一個有爭議的問題, 因爲C99需要一個函數聲明是在 點呼叫可見(默認聲明不再提供)

問題2:

如何將代碼分解成2個文件編譯?

它會編譯,它會像第一個問題表示exactelly相同