2010-04-13 149 views
30

我一直在C工作了很長時間,編譯器通常會在extern的開頭添加一個下劃線的事實只是理解......但是,another SO question today讓我想知道爲什麼添加下劃線的真正原因。一個宣稱的理由是:爲什麼C編譯器會在外部名稱前加下劃線?

這對C編譯器前面加上一個前導下劃線所有外部範圍的節目標識符,以避免衝突與運行語言支持貢獻

我覺得有常見的做法至少這是真相的一個核心,但似乎也沒有真正回答這個問題,因爲如果將下劃線添加到所有的extern中,對於防止衝突沒有多大幫助。

有沒有人有關於領導下劃線的基本原理的良好信息?

Unix creat()系統調用不是以'e'結尾的原因是否增加了下劃線部分?我聽說某些平臺上的早期連接器對名稱有6個字符的限制。如果是這樣的話,那麼對外部名稱加下劃線似乎是一個徹底的瘋狂想法(現在我只有5個角色可以玩......)。

+2

應當指出的是,這種行爲是不是現代ELF系統實踐。顯然,它在aout/coff平臺上很常見。 – 2010-12-07 01:33:43

+0

Clang爲什麼在OS X上執行它?我怎樣才能關閉它? – MarcusJ 2016-05-29 02:07:24

回答

17

這是常見的做法C編譯器前面加上一個前導下劃線所有外部範圍的節目標識符,以避免與運行語言支持

貢獻衝突如果運行時支持是由編譯器提供的,你會認爲在運行時支持中爲少數外部標識符預先加下劃線會更有意義!

當C編譯器第一次出現時,在這些平臺上用C語言進行編程的基本替代方法是使用匯編語言進行編程,並且它有時(並且偶爾仍然是)將彙編器和C編寫的目標文件鏈接在一起非常有用。恕我直言)前導下劃線添加到外部C標識符是爲了避免與您自己的彙編代碼中的標識衝突。

(也GCC's asm label extension見;並注意這個預謀下劃線可以考慮名稱的簡單形式壓延像C更復雜的語言++使用更復雜的名字改編,但是這是它開始。)

+0

我喜歡諷刺性的「GCC目前還沒有能力在寄存器中存儲靜態變量,也許這將被添加。」在鏈接的文件中發表評論。 – 2010-04-13 18:59:28

+0

@MichaelBurr:這可能不是諷刺。在某些系統中,您可以保留一個全局寄存器作爲指向某個內存區域的指針(例如,在ARM EABI的靜態基址指針的某些變體中的'R9')。 – 2013-09-12 22:56:37

3

從我總是聽到的是避免命名衝突。不適用於其他外部變量,但更重要的是,當您使用庫時,它最好不會與用戶代碼變量名稱發生衝突。

1

Wikipedia

這是常見的做法C編譯器前面加上一個前導下劃線所有外部範圍的節目標識符,以避免與運行語言支持貢獻的衝突。此外,當C/C++編譯器需要在外部鏈接中引入名稱作爲翻譯過程的一部分時,這些名稱通常會以多個前導或末尾下劃線的組合來區分。

這種做法後來被編纂爲C和C++語言標準的一部分,其中使用前導下劃線是爲實施保留的。
3

主函數不是可執行文件的真實入口點。一些靜態鏈接的文件具有最終調用main的真正入口點,而那些靜態鏈接的文件擁有不以下劃線開頭的名稱空間。在我的系統上,在/ usr/lib中,有gcrt1.o,crt1.o和dylib1.o等等。每個人都有一個「開始」功能,沒有下劃線,最終會調用「_main」入口點。除這些文件外的其他所有內容都有外部範圍歷史與在一個項目中混合彙編器和C有關,所有C被認爲是外部的。

5

如果c編譯器總是在每個符號前面加上下劃線,則啓動/ c運行時代碼(通常以彙編語言編寫)可以安全地使用不以下劃線開頭的標籤和符號(例如符號'開始')。

即使您在c代碼中編寫了start()函數,它也會在對象/ asm輸出中生成爲_start。 (請注意,在這種情況下,c代碼不可能生成不以下劃線開頭的符號),因此啓動編碼器不必擔心爲每個符號創建不明顯的不可能符號(如$ _dontuse42%$)他/她的全局變量/標籤。

所以鏈接器不會抱怨名稱衝突,程序員很高興。 :)

以下不同於編譯器在其輸出格式中預先加下劃線的做法。

這種做法後來被編纂爲C和C++語言標準的一部分,其中前導下劃線的使用被保留用於執行。

對於c系統庫和其他系統組件,這是一個慣例。 (以及諸如__FILE__之類的東西)。

(請注意,這樣的符號(例如:_time)可能導致生成的輸出2個領先的下劃線(__time))

相關問題