[英]C - One-definition rule for functions
我是 C 的新手,讀到每個 function 可能只定義一次,但我似乎無法將其與我在控制台中看到的內容相協調。 例如,我能夠在沒有錯誤或警告的情況下覆蓋printf
的定義:
#include <stdio.h>
extern int printf(const char *__restrict__format, ...) {
putchar('a');
}
int main() {
printf("Hello, world!");
return 0;
}
因此,我嘗試查找標准中的單一定義規則,並在第 155 頁上找到第 6.9 (5) 節,其中說(強調):
外部定義是外部聲明,它也是 function(內聯定義除外)或 object 的定義。如果在表達式 [...] 中使用使用外部鏈接聲明的標識符,則在整個程序的某處應恰好是該標識符的一個外部定義; 否則,不得超過一個。
我對鏈接的理解非常不穩定,所以我不確定這是相關的條款還是“整個程序”的確切含義。 但是,如果我將“整個程序”理解為<stdio.h>
中的所有內容 + 我的源文件,那么我是否應該被禁止在我的源文件中重新定義printf
,因為它已經在“整個程序”的早期定義"(即在程序的stdio
位中)?
如果這個問題是一個騙局,我很抱歉,我找不到任何現有的答案。
C 標准沒有定義如果 function 有多個定義會發生什么。
……我不應該被禁止嗎……
C 標准對您的操作沒有管轄權。 它指定了 C 程序的解釋方式,而不是人類的行為方式。 雖然它的一些規則是用“shall”來寫的,但這並不是對程序員的命令,告訴他們他們可以做什么或不可以做什么。 它是一種修辭手段,用於指定 C 程序的語義。 C 2018 4 2 告訴我們它的實際含義:
如果違反了出現在約束或運行時約束之外的“應該”或“不應”要求,則行為未定義......
因此,當您提供printf
的定義並且標准 C 庫提供 printf 的定義時, printf
標准未指定會發生什么。 在通常的實踐中,可能會發生以下幾種情況:
printf
。 庫中的printf
沒有用到。printf
的內置知識並使用它,盡管您定義了printf
。printf
在一個單獨的源模塊中,並且該模塊被編譯並插入到一個庫中,那么程序使用哪個printf
取決於庫指定給 linker 的順序。雖然 C 標准沒有定義如果 function(或一般的外部符號)有多個定義會發生什么,鏈接器通常會這樣做。 通常,當一個 linker 處理一個庫文件時,它的行為是:
因此,對於普通函數,出現在庫文件中的多個定義的行為是由 linker 定義的,即使它不是由 C 標准定義的。 (不過,可能會有一些復雜的情況。假設一個程序使用cos
和sin
,並且 linker 已經包含一個定義cos
的模塊,當它找到一個同時定義sin
和cos
的庫模塊時。因為 linker 有一個未解析的對sin
的引用,它包括這個庫模塊,它引入了cos
的第二個定義,導致多重定義錯誤。)
盡管 linker 的行為可能定義得很好,但這仍然存在編譯器具有關於標准庫函數的內置知識的問題。 考慮這個例子。 在這里,我添加了第二個printf
,所以程序有:
printf("Hello, world!");
printf("Hello, world!\n");
程序 output 是“aHello, world.\n”。 這表明程序對第一個printf
調用使用了您的定義,但對第二個printf
調用使用了標准行為。 該程序的行為就好像在同一個程序中有兩個不同的printf
定義。
查看匯編語言可以看出發生了什么。 對於第二次調用,編譯器決定,因為printf("Hello, world;\n");
正在打印一個沒有轉換規范並以換行符結尾的字符串,它可以改用更高效的puts
例程。 所以匯編語言對第二個printf
有call puts
。 編譯器無法對第一個printf
執行此操作,因為它沒有以換行符結尾,它puts
自動添加。
請注意declaration
和definition
。 術語完全不同。
declaration
。 因此,當您在文件中聲明/定義時,只要原型相似,就可以了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.