2013-10-13 38 views
3

如果不包含並使用malloc,我們會得到隱式聲明警告。隱式聲明如何工作

「警告:內建函數‘的malloc’不兼容的隱式聲明」

這個警告是由於事實,編譯器假定定義爲int的malloc(大小)的malloc而這是void* malloc(size)

但是它怎麼知道void* malloc(size)?我們沒有在頭文件中包含任何內容。那麼它如何與未包括的東西進行比較呢?

之後,我的代碼如何工作?它如何找到正確的malloc定義並使用它?

是否有函數定義被掃描的序列順序?

+2

您是否注意到該警告中的「built-in」一詞? – rici

回答

5

當你調用一個從未被定義的函數f,這樣一個隱含的聲明發生:

int f(); 

請注意,您還可以通過參數f,因爲它並沒有宣佈int f(void);(這是爲了與K & RC向後兼容)。

因此,編譯器不會「知道」malloc接收到大小參數,事實上,您可以通過它,你想要什麼,它不關心。

因此,代碼工作的純粹事實是純粹的運氣。在malloc的情況下,如果代碼正常工作,它意味着整數的大小與指針的大小相同 - 不多也不少 - 因此,您仍然可以調用malloc並將其結果指定給指針,因爲沒有位被修剪/丟失。

真正的功能是在編譯發生後的鏈接階段找到的。這時,你的代碼已經被編譯了一個錯誤的函數原型。當然,如果鏈接程序無法在其路徑的任何位置找到該函數,則會報告錯誤並且一切都會中止。

對於malloc和其他標準庫函數的情況,可能會有內置函數來提高性能。 gcc甚至可以選擇禁用內置函數(​​或-fno-builtin-function)。從海灣合作委員會手冊頁:

GCC normally generates special code to handle certain built-in functions more efficiently; for instance, calls to "alloca" may become single instructions that adjust the stack directly, and calls to "memcpy" may become inline copy loops. The resulting code is often both smaller and faster, but since the function calls no longer appear as such, you cannot set a breakpoint on those calls, nor can you change the behavior of the functions by linking with a different library. In addition, when a function is recognized as a built-in function, GCC may use information about that function to warn about problems with calls to that function, or to generate more efficient code, even if the resulting code still contains calls to that function.

所以,在的malloc的特定情況下,這是編譯器如何「知道」其一貫的簽名。嘗試編譯此代碼用gcc:

int main(void) { 
    char *a = malloc(12, 13, 14, 15); 
    return 0; 
} 

你會看到它有一個編譯錯誤中止:

test.c:3: error: too many arguments to function `malloc' 

如果您使用的選項-fnobuiltin,錯誤消失和警告是不同的:

test.c:3: warning: implicit declaration of function `malloc' 

這是你每次都遇到相同的警告你使用沒有以前定義的常規功能,因爲現在的編譯器是忽略了他「知道」了se功能。這個例子使用gcc,但其他編譯器也會有類似的行爲。

+1

我認爲問題在於編譯器如何知道'malloc'具有不同的簽名,而不是代碼的工作原理。 – templatetypedef

+0

@templatetypedef好點。我在我的答案中增加了更多信息。 –

+0

@FilipeGonçalves您能否詳細說明一下,編譯器如何知道其通常的簽名?你的意思是,gcc有一個內置的數據庫(我的意思是列表或知識體)的聲明和函數的定義,如malloc,printf等?謝謝你的支持。 – sakura

0

如果您沒有正確聲明它(無論是在源代碼中顯式聲明,還是通過包含正確的標頭),它都找不到正確的定義。它對聲明之前調用的任何函數使用默認的隱式聲明。隱式聲明包含該函數返回int的假設。

然後將默認聲明與您稱爲函數的方式進行比較。它注意到你將結果分配給一個指針變量而不是一個整數,所以它警告你這些是不兼容的。

+0

好的,但仍然[this](http://stackoverflow.com/questions/19350120/how-implicit-declaration-works#comment28669450_19350120)。即使沒有對它的單獨調用,編譯器也知道'malloc()'的簽名,因爲它被識別爲一個硬編碼的內建函數。 – 2013-10-13 21:08:27

1

But how does it know about void* malloc(size) ? We have not included anything in header files. So how is it comparing it against something that is not included.

大多數現代編譯器有共同的標準庫函數內置(即硬編碼在編譯器)列表中,因此編譯器知道該聲明應該什麼即使沒有聲明的功能(顯式或隱式)或調用。

這並不意味着將使用正確的聲明(因爲隱式聲明規則會覆蓋編譯器的明顯知識),但至少您知道自己做錯了什麼。

這些內置函數的確切目的是,如果忘記包含頭文件,編譯器可以提醒您。 (還有其他目的,例如通過了解內置函數的語義/實現來執行固有優化的機會,但這些不適用於此)。

1

您得到的原因特定警告是malloc是一個內置函數在這個特定的編譯器(作爲警告狀態)。該函數接受編譯器的特殊處理。編譯器具有關於如何聲明該函數的內在知識,所以當隱式聲明與正確的聲明相矛盾時,會立即發出警告。

在一般情況下,對於普通(非內置)函數,如果編譯器實際發現同一函數的第一個聲明爲聲明,那麼隱式聲明的函數將會類似(假設存在不匹配兩者之間)。在這種情況下,警告將在稍後發佈:不是在呼叫點,而是在明確聲明的地方。這是它與普通函數一起工作的方式。

但是由於malloc這樣的標準(內置)函數,編譯器會讓自己有點「作弊」。