我是 C 的新手,讀過每個函式只能定義一次,但我似乎無法將其與我在控制臺中看到的內容相協調。例如,我能夠在printf
沒有錯誤或警告的情況下覆寫的定義:
#include <stdio.h>
extern int printf(const char *__restrict__format, ...) {
putchar('a');
}
int main() {
printf("Hello, world!");
return 0;
}
因此,我嘗試查找標準中的單一定義規則,并在第 155 頁上找到第 6.9 (5) 節,其中說(強調):
外部定義是外部宣告,也是函式(行內定義除外)或物件的定義。如果在運算式 [...] 中使用了使用外部鏈接宣告的識別符號,則在整個程式的某處,該識別符號應該只有一個外部定義;否則,不得超過一個。
我對鏈接的理解非常不穩定,所以我不確定這是相關的條款還是“整個程式”的確切含義。但是,如果我將“整個程式”表示為<stdio.h>
我的源檔案中的所有內容,那么我不應該被禁止printf
在我的源檔案中重新定義,因為它已經在“整個程式”中定義過(stdio
即位程式)?
如果這個問題是一個騙局,我很抱歉,我找不到任何現有的答案。
uj5u.com熱心網友回復:
C 標準沒有定義如果一個函式有多個定義會發生什么。
……我不應該被禁止嗎……
C 標準對你所做的沒有管轄權。它指定了 C 程式的解釋方式,而不是人類的行為方式。雖然它的一些規則是用“shall”來寫的,但這并不是對程式員的命令,告訴他們他們可以做什么或不可以做什么。它是一種用于指定 C 程式語意的修辭手段。C 2018 4 2 告訴我們它的實際含義:
如果違反了出現在約束或運行時約束之外的“應該”或“不應”要求,則行為未定義......
因此,當您提供 的定義printf
并且標準 C 庫提供 的定義時printf
,C 標準沒有指定會發生什么。在通常的實踐中,可能會發生以下幾種情況:
- 聯結器使用您的
printf
. 庫printf
中的 未使用。 printf
盡管您定義了printf
.- 如果你
printf
在一個單獨的源模塊中,并且該模塊被編譯并插入到一個庫中,那么printf
程式使用哪個取決于庫被指定給聯結器的順序。
雖然 C 標準沒有定義如果一個函式(或一般的外部符號)有多個定義會發生什么,但聯結器通常會這樣做。通常,當聯結器處理庫檔案時,其行為是:
- 檢查庫中的每個模塊。如果該模塊定義了一個由先前并入的目標模塊參考但尚未定義的符號,則將該模塊包含在聯結器正在構建的輸出中。如果模塊未定義任何此類符號,則不要使用它。
因此,對于普通函式,出現在庫檔案中的多個定義的行為是由聯結器定義的,即使它不是由 C 標準定義的。(不過,這可能會很復雜。假設一個程式使用cos
and sin
,并且聯結器已經包含一個模塊,cos
當它找到一個同時定義sin
和的庫模塊時定義cos
。因為聯結器有一個未決議的參考sin
,它包含這個庫模塊,它引入了 的第二個定義cos
,導致多重定義錯誤。)
盡管聯結器的行為可能定義得很好,但這仍然存在編譯器具有關于標準庫函式的內置知識的問題。考慮這個例子。在這里,我添加了第二個printf
,所以程式有:
printf("Hello, world!");
printf("Hello, world!\n");
程式輸出是“aHello, world.\n”。這表明程式在第一次printf
呼叫時使用了您的定義,但在第二次呼叫時使用了標準行為printf
。該程式的行為就好像printf
在同一個程式中有兩個不同的定義。
查看匯編語言可以看出發生了什么。對于第二次呼叫,編譯器決定,由于printf("Hello, world!\n");
正在列印一個沒有轉換規范且以換行符結尾的字串,因此它可以使用更高效的puts
例程。所以匯編語言有call puts
第二個printf
。編譯器無法首先執行此操作,因為它不以自動添加printf
的換行符結尾。puts
uj5u.com熱心網友回復:
請注意declaration
和definition
。術語完全不同。
- stdio.h 只提供
declaration
. 因此,當您在檔案中宣告/定義時,只要原型相似,就可以了。 - 您可以在源檔案中自由定義。如果可用,最終程式將鏈接到您的而不是庫中的程式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/537596.html
標籤:C功能外部的函数定义连锁