2015-07-13 17 views
0

如果我定義了一個函數兩次,我會得到一個重定義錯誤信息,但是我很困惑重新定義發生在編譯或鏈接時間?重新定義在哪個程序中發生?編譯或鏈接時間?

爲什麼你可以覆蓋malloc libc沒有重定義錯誤?

+2

重新定義在鏈接階段發生。 – Ishmeet

+0

是的,您可以覆蓋malloc(),因爲如果符號尚未被您自己的代碼滿足,鏈接器只會查看libc。通用建議:除非你真的*知道你在做什麼,否則不要試用。 – DevSolar

回答

0

其中之一或兩者。它也可能源於程序員編輯源代碼或修改構建腳本。

當鏈接器找到兩個具有相同名稱的東西(符號)時,會發出「重新定義」錯誤。

鏈接器可能會找到兩個具有相同名稱的符號的原因很多。一些可能性(有許多排列)包括;

  • 目標文件在鏈接命令中指定了兩次。這通常是由構建腳本中的錯誤造成的。
  • 兩個包含相同函數定義的目標文件。這是代碼複製的結果 - 例如,函數定義被複制到不同的源文件中,然後進行編譯和鏈接。它也可能來自與預處理器的猴業務(例如#include ing文件包含由兩個源文件定義的全局變量)。

的東西,如上述的原因通常是程序員錯誤(例如,在構建腳本供給壞接頭命令,項目之間的預處理器,複製和粘貼代碼的誤用。

原因在庫中的函數像libc通常可以被「重載」,就是鏈接器通常只在庫中找到符號,如果它在對象文件中找不到它們的話,那麼,如果一個目標文件定義了malloc(),鏈接器將解析所有的調用,而不是嘗試在庫中使用版本來解析這種事情是相當危險的,因爲庫中的一些其他函數(例如,甚至在libc中)可能直接解析爲原始的malloc()(例如一些調用可能被內聯),這可能會導致不可預知的行爲。這種行爲也是特定於鏈接器的:雖然這種情況在unix/linux變體中很常見,但有些系統中連接器的做法各不相同。

1

如果您有兩個具有相同原型或簽名的函數(函數簽名由參數和參數類型的函數名稱數量構成,不包含返回類型),則會得到函數重定義錯誤。

這是一個編譯時錯誤,如果編譯器看到兩個函數具有相同簽名:

int foo(int a); 
double foo(int b); 

爲什麼你可以重寫庫函數調用?讓我們來看看代碼是如何建成的可執行文件:

  • 編譯器被稱爲爲每個源文件和輸出的目標文件:任何函數調用不能被解析(即調用在不同文件中的函數)鏈接器必須解決的外部符號。
  • 鏈接器獲取所有對象文件並嘗試解析所有符號;但它是以先到先得的方式完成的。對於外部符號,它會考慮它找到的第一個定義,而不用擔心可能存在更多相同符號的定義。

因此,鏈接器實際上允許您覆蓋函數的行爲。這一切都取決於文件鏈接的順序 - 它找到的第一個函數定義是用於解析符號的函數。

希望對此有所瞭解。

+0

謝謝Pandrei, 做的「先到先得」方式只適用於共享庫嗎? 這裏是我想要的,main.cpp調用def1.cpp和def2.cpp中定義的函數: gcc main.cpp def1.cpp def2.cpp //多重定義錯誤 但是 gcc main.cpp def1。所以def2.so //是的,先到先得的方式 有什麼區別? –

+0

@ SamH.W不,它不適用於圖書館;實際上鍊接器並不知道區別;它只接收一堆目標文件並嘗試解析這些符號。試試這個:一個帶有函數原型的頭文件,兩個源文件,每個帶有它自己的實現,並在main()中調用該函數。即使您對同一個函數有兩個不同的實現,也應該創建它。 – Pandrei

相關問題