1. 程式人生 > >幫 C/C++ 程式設計師徹底瞭解連結器

幫 C/C++ 程式設計師徹底瞭解連結器

本文旨在幫助 C/C++ 程式設計師們瞭解連結器到底完成了些什麼工作。多年來,我給許多同事解釋過這一原理,因此我覺得是時候把它寫下來了,這樣不僅可以供更多人學習,也省去我一遍遍講解。

[2009年3月更新,內容包括:增加了 Windows 系統中連結過程可能遇到的特殊問題,以及對某條定義規則的澄清。]

促使我寫下這篇文章的起因是某次我幫人解決了一個連結錯誤,具體是這樣的:

1 2 3 4 g++ -o test1 test1a.o test1b.o test1a.o(.text+0x18): In function `main':
: undefined reference to `findmax(int, int)' collect2: ld returned 1 exit status

目錄

各部分的命名:看看 C 檔案中都包含了哪些內容

本章,我們將快速回憶一下 C 檔案中包含的幾大部分。如果你認為自己已經完全明白下文示例程式中的內容,那麼你可以跳過本章,直接閱讀下一章

我們首先要弄清的是宣告和定義的區別。定義(definition)是指建立某個名字與該名字的實現之間的關聯,這裡的“實現”可以是資料,也可以是程式碼:

  • 變數的定義,使得編譯器為這個變數分配一塊記憶體空間,並且還可能為這塊記憶體空間填上特定的值
  • 函式的定義,使得編譯器為這個函式產生一段程式碼

宣告(declaration)是告訴 C 編譯器,我們在程式的別處——很可能在別的 C 檔案中——以某個名字定義了某些內容(注意:有些時候,定義也被認為是宣告,即在定義的同時,也在此處進行了宣告)。

對於變數而言,定義可以分為兩種:

  • 全域性變數(global variables):其生命週期存在於整個程式中(即靜態範圍(static extent)),可以被不同的模組訪問
  • 區域性變數(local variables):生命週期只存在於函式的執行過程中(即區域性範圍(local extent)),只能在函式內部訪問

澄清一點,我們這裡所說的“可訪問(accessible)”,是指“可以使用該變數在定義時所起的名字”。

以下是幾個不太直觀的特殊情況:

  • 用 static 修飾的區域性變數實際上是全域性變數,因為雖然它們僅在某個函式中可見,但其生命週期存在於整個程式中
  • 同樣,用 static 修飾的全域性變數也被認為是全域性的,儘管它們只能由它們所在的檔案內的函式訪問

當我們談及 “static” 關鍵字時,值得一提的是,如果某個函式(function)用 static 修飾,則該函式可被呼叫的範圍就變窄了(尤其是在同一個檔案中)。

無論定義全域性變數還是區域性變數,我們可以分辨出一個變數是已初始化的還是未初始化的,分辨方法就是這個變數所佔據的記憶體空間是否預先填上了某個特殊值。

最後要提的一點是:我們可以將資料存於用 malloc 或 new 動態分配的記憶體中。這部分記憶體空間沒法通過變數名來訪問,因此我們使用指標(pointer)來代替——指標也是一種有名字的變數,它用來儲存無名動態記憶體空間的地址。這部分記憶體空間最終可以通過使用 free 和 delete 來回收,這也是為什麼將這部分空間稱為“動態區域”(dynamic extent)。

讓我們來總結一下吧:

程式碼 資料
全域性 區域性 動態
已初始化 未初始化 已初始化 未初始化
宣告 int fn(int x); extern int x; extern int x; N/A N/A N/A
定義 int fn(int x) { … } int x = 1; (作用域:檔案) int x; (作用域:檔案) int x = 1; (作用域:函式) int x; (作用域:函式) (int* p = malloc(sizeof(int));)

以下是一個示例程式,也許是一種更簡便的記憶方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 /* 這是一個未初始化的全域性變數的定義 */ int x_global_uninit; /* 這是一個初始化的全域性變數的定義 */ int x_global_init = 1; /* 這是一個未初始化的全域性變數的定義,儘管該變數只能在當前 C檔案中訪問 */ static int y_global_uninit; /* 這是一個初始化的全域性變數的定義,儘管該變數只能在當前 C檔案中訪問 */

相關推薦

FW: C/C++ 程式設計師徹底瞭解連結

幫 C/C++ 程式設計師徹底瞭解連結器 2015/12/18 · C/C++, 開發 · 3 評論 · C語言, 連結器   本文由 伯樂線上 - 小胖妞妞 翻譯, 黃小非 校稿。未經許可,禁止轉載! 英文出處:

C/C++ 程式設計師徹底瞭解連結

本文旨在幫助 C/C++ 程式設計師們瞭解連結器到底完成了些什麼工作。多年來,我給許多同事解釋過這一原理,因此我覺得是時候把它寫下來了,這樣不僅可以供更多人學習,也省去我一遍遍講解。 [2009年3月更新,內容包括:增加了 Windows 系統中連結過程可能遇到

C++初級程式設計師筆試題總結

轉行碼農一年多了,也經歷過許多筆試面試,好好總結一下C++初級程式設計師筆試常見的問題,供大家參考,不足之處請大家補充,有什麼問題也可留言。 也算列個提綱,後續逐一來填坑。 適用物件:初級程式設計師。 以下順序有遞進之意味。   0、基礎語法題   考察C++基礎是否紮實:主要針對變數和

窺探C語言程式的編譯、連結

窺探C語言程式的編譯、連結 C語言程式從原始檔到生成可執行程式需要經過編譯器的預處理,彙編、編譯、連結。這裡簡化為編譯、連結兩個階段 編譯:對原始檔語法做檢查 連結:會查詢函式的具體實現並把專案中所有相關的.o目標檔案、C語言函式庫合併在一起生成可執行檔案 我來編寫一個main.c 對main.c

如何成為一個C++高階程式設計師

C++這門語言從誕生到今天已經經歷了將近30個年頭。不可否認,它的學習難度都比其它語言較高。而它的學習難度,主要來自於它的複雜性。現在C++的使用範圍比以前已經少了很多,java、C#、python等語言在很多方面已經可以代替C++。但是也有很多地方是其他語言完全無法替代的,主要集中在需要執行效率比較高的行業

如何成為一名C++高階程式設計師

C++這門語言從誕生到今天已經經歷了將近30個年頭。不可否認,它的學習難度都比其它語言較高。而它的學習難度,主要來自於它的複雜性。現在C++的使用範圍比以前已經少了很多,java、C#、python等語言在很多方面已經可以代替C++。但是也有很多地方是其他語言完全無法替代的,主要集中在需要執

C語言程式的編譯、連結具體過程及Linux環境下的具體例項

我們知道一個C語言程式從編寫完成到生成可執行檔案分為預處理、編譯、彙編、連結,最後再生成可執行檔案。 預處理階段 前處理器主要做四部分工作,分別是:標頭檔案展開、巨集替換、去註釋以及條件編譯。 (1)標頭檔案展開就是編譯器會根據字元#開頭的命令去讀取相

c++討論會 永遠不要相信那些自稱懂C++的程式設計師 (Never trust a programmer who says h

I’ve been in an interviewing mindset for awhile and I’ve come to realize something important about C++ in particular. C++ is a “two peak” lang

如何成為一個C++高階程式設計師(收藏)

C++這門語言從誕生到今天已經經歷了將近30個年頭。不可否認,它的學習難度都比其它語言較高。而它的學習難度,主要來自於它的複雜性。現在 C++ 的使用範圍比以前已經少了很多,java、C#、python 等語言在很多方面已經可以代替C++。但是也有很多地方是其他語言完全無

C語言程式設計師的七夕如何表白:用C說愛你

有一封信,送給有你的七夕。 總想為這1385個日夜寫點什麼,卻是提筆思念,落筆無言。 我只是一個平凡的C語言程式設計師,請你原諒我的笨,我願給你我的心。我不會甜言蜜語,只會用C說愛你。   “浪跡過你的浪跡,珍惜著你的珍惜”   “月光是海的夢境,而你是我的遠行”

Java匹馬行天下之C程式設計師的禿頭原因

  Java帝國的崛起 前言: 分享技術之前先請允許我分享一下黃永玉老先生說過的話:“明確的愛,直接的厭惡,真誠的喜歡。站在太陽下的坦蕩,大聲無愧地稱讚自己。” 《程式設計常識知多少》 《走進程式設計的殿堂》 《高階語言大鍋燴》 前面寫的三篇部落格如果有朋友認真

五張圖你辨別程式設計師中的菜鳥、普通、大牛和真大神!

這屆的沙雕網友太厲害了,自創程式設計師鑑定套圖,菜鳥、普通、大牛到大神四個級別的程式設計師,分別被劃分為四個座標象限,真是太有畫面感了~ 每天穿拖鞋背心,看起來吊兒郎當的,往往是程式設計師裡的“掃地僧” 菜鳥程式設計師不出么蛾子,團隊怎麼會有事做 緣分到了,BUG自然就

Java程式設計師必須瞭解的幾款錯誤跟蹤工具

Java是計算機應用程式程式語言,被廣泛用於建立Web應用、伺服器處理、使用者端的API開發乃至資料庫等多個領域。 隨著Java生態系統的發展,使用者效能需求的提高,一類新的開發工具應運而生。與傳統方法相比,它們既可以滿足在一個快節奏的環境中快速部署所需要的錯誤跟蹤需求,又

掌握這個技能,你延長程式設計師的職業“生命週期”~

程式設計師一直是一個神祕的群體,在外人看來神祕莫測的程式設計師們經常以加班熬夜為家常便飯。不!其實是比家常便飯還頻繁的存在。 他們在屏幕後面敲出一行又一行讓人看不懂得程式碼,而這些程式碼卻可以帶領人們進入新的世界! 可是隻有程式設計師才真的瞭解程式設計師的痛

作為程式設計師應該瞭解的32個演算法(持續更新)

演算法就是解決問題的一種模式,通過演算法我們可以更輕鬆快速的解決問題。作為程式設計師,我們應該熟練掌握一些演算法並瞭解多個演算法。 我會持續不斷的更新這篇文章,爭取把這些演算法都給描述一下,用自己的思維方式給大家講解一下。 來自百度百科: 演算法(Algori

Java程式設計師應該瞭解的10個面向物件設計原則

摘要:Java程式設計最基本的原則就是要追求高內聚和低耦合的解決方案和程式碼模組設計。檢視Apache和Sun的開放原始碼能幫助你發現其他Java設計原則在這些程式碼中的實際運用。 面向物件設計原則是OOPS(Object-Oriented Programming Sys

優秀Java程式設計師應該瞭解的GC工作原理

一個優秀的Java程式設計師必須瞭解GC的工作原理、如何優化GC的效能、如何與GC進行有限的互動,因為有一些應用程式對效能要求較高,例如嵌入式系統、實時系統等,只有全面提升記憶體的管理效率 ,才能提高整個應用程式的效能。 一個優秀的Java程式設計師必須瞭解GC的工作原理、如何優化GC的效能、

優秀Java程式設計師必須瞭解的GC工作原理 侵立刪

一個優秀的Java程式設計師必須瞭解GC的工作原理、如何優化GC的效能、如何與GC進行有限的互動,因為有一些應用程式對效能要求較高,例如嵌入式系統、實時系統等,只有全面提升記憶體的管理效率 ,才能提高整個應用程式的效能。 一個優秀的Java程式設計師必須瞭解GC的工作原理、如何優化GC的效能、

每個程式設計師應該瞭解的97件事情

原文:http://dearymz.blog.163.com/blog/static/205657420139243750104/ 正文之前 熟知軟體開發的人都知道這個行業裡充滿了一次次悲壯的失敗,每一座成功專案的豐碑下都埋葬著無數同類型的失敗專案。大多數軟體專案都像是

程式設計師必須瞭解的記憶體知識

C和C++語言開發中,指標、記憶體一直是學習的重點。因為C語言作為一種偏底層的中低階語言,提供了大量的記憶體直接操作的方法,這一方面使程式的靈活度最大化,同時也為bug埋下很多隱患。 因此,無論如何,我們都要對記憶體有一個清晰的理解。 1. 對記憶體的分配 32位作