日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 充電吧
[導讀]?這里并沒不是討論大學課程中所學的《編譯原理》,只是寫一些我自己對C++編譯器及鏈接器的工作原理的理解和看法吧,以我的水平,還達不到講解編譯原理(這個很復雜,大學時幾乎沒學明白)。要明白的幾個概念:?

?這里并沒不是討論大學課程中所學的《編譯原理》,只是寫一些我自己對C++編譯器及鏈接器的工作原理的理解和看法吧,以我的水平,還達不到講解編譯原理(這個很復雜,大學時幾乎沒學明白)。

要明白的幾個概念:

????1、編譯:編譯器對源文件進行編譯,就是把源文件中的文本形式存在的源代碼翻譯成機器語言形式的目標文件的過程,在這個過程中,編譯器會進行一系列的語法檢查。如果編譯通過,就會把對應的CPP轉換成OBJ文件。

????2、編譯單元:根據C++標準,每一個CPP文件就是一個編譯單元。每個編譯單元之間是相互獨立并且互相不可知。

????3、目標文件:由編譯所生成的文件,以機器碼的形式包含了編譯單元里所有的代碼和數據,還有一些期他信息,如未解決符號表,導出符號表和地址重定向表等。目標文件是以二進制的形式存在的。

?

????根據C++標準,一個編譯單元(Translation Unit)是指一個.cpp文件以及這所include的所有.h文件,.h文件里面的代碼將會被擴展到包含它的.cpp文件里,然后編譯器編譯該.cpp文件為一個.obj文件,后者擁有PE(Portable Executable,即Windows可執(zhí)行文件)文件格式,并且本身包含的就是二進制代碼,但是不一定能執(zhí)行,因為并不能保證其中一定有main函數。當編譯器將一個工程里的所有.cpp文件以分離的方式編譯完畢后,再由鏈接器進行鏈接成為一個.exe或.dll文件。

?

下面讓我們來分析一下編譯器的工作過程:

我們跳過語法分析,直接來到目標文件的生成,假設我們有一個A.cpp文件,如下定義:

????int n = 1;

????void FunA()

????{

????????++n;

????}

?

????它編譯出來的目標文件A.obj就會有一個區(qū)域(或者說是段),包含以上的數據和函數,其中就有n、FunA,以文件偏移量形式給出可能就是下面這種情況:

????偏移量????內容????長度

????0x0000????n???????4

????0x0004????FunA??????

????注意:這只是說明,與實際目標文件的布局可能不一樣,??表示長度未知,目標文件的各個數據可能不是連續(xù)的,也不一定是從0x0000開始。

????FunA函數的內容可能如下:

????0x0004 inc DWORD PTR[0x0000]

????0x00?? ret

????這時++n已經被翻譯成inc DWORD PTR[0x0000],也就是說把本單元0x0000位置的一個DWORD(4字節(jié))加1。

?

????有另外一個B.cpp文件,定義如下:

????extern int n;

????void FunB()

????{

????????++n;

????}

????它對應的B.obj的二進制應該是:

????偏移量????內容????長度

????0x0000????FunB??????

????這里為什么沒有n的空間呢,因為n被聲明為extern,這個extern關鍵字就是告訴編譯器n已經在別的編譯單元里定義了,在這個單元里就不要定義了。由于編譯單元之間是互不相關的,所以編譯器就不知道n究竟在哪里,所以在函數FunB就沒有辦法生成n的地址,那么函數FunB中就是這樣的:

????0x0000 inc DWORD PTR[????]

????0x00?? ret

????那怎么辦呢?這個工作就只能由鏈接器來完成了。

????為了能讓鏈接器知道哪些地方的地址沒有填好(也就是還????),那么目標文件中就要有一個表來告訴鏈接器,這個表就是“未解決符號表”,也就是unresolved symbol table。同樣,提供n的目標文件也要提供一個“導出符號表”也就是exprot symbol table,來告訴鏈接器自己可以提供哪些地址。

?

????好,到這里我們就已經知道,一個目標文件不僅要提供數據和二進制代碼外,還至少要提供兩個表:未解決符號表和導出符號表,來告訴鏈接器自己需要什么和自己能提供些什么。那么這兩個表是怎么建立對應關系的呢?這里就有一個新的概念:符號。在C/C++中,每一個變量及函數都會有自己的符號,如變量n的符號就是n,函數的符號會更加復雜,假設FunA的符號就是_FunA(根據編譯器不同而不同)。

????所以,

????A.obj的導出符號表為

????符號????地址

????n???????0x0000

????_FunA???0x0004

????未解決符號為空(因為他沒有引用別的編譯單元里的東西)。

????B.obj的導出符號表為

????符號????地址

????_FunB???0x0000

????未解決符號表為

????符號????地址

????n???????0x0001

????這個表告訴鏈接器,在本編譯單元0x0001位置有一個地址,該地址不明,但符號是n。

????在鏈接的時候,鏈接在B.obj中發(fā)現了未解決符號,就會在所有的編譯單元中的導出符號表去查找與這個未解決符號相匹配的符號名,如果找到,就把這個符號的地址填到B.obj的未解決符號的地址處。如果沒有找到,就會報鏈接錯誤。在此例中,在A.obj中會找到符號n,就會把n的地址填到B.obj的0x0001處。

?

????但是,這里還會有一個問題,如果是這樣的話,B.obj的函數FunB的內容就會變成inc DWORD PTR[0x000](因為n在A.obj中的地址是0x0000),由于每個編譯單元的地址都是從0x0000開始,那么最終多個目標文件鏈接時就會導致地址重復。所以鏈接器在鏈接時就會對每個目標文件的地址進行調整。在這個例子中,假如B.obj的0x0000被定位到可執(zhí)行文件的0x00001000上,而A.obj的0x0000被定位到可執(zhí)行文件的0x00002000上,那么實現上對鏈接器來說,A.obj的導出符號地地址都會加上0x00002000,B.obj所有的符號地址也會加上0x00001000。這樣就可以保證地址不會重復。

?

????既然n的地址會加上0x00002000,那么FunA中的inc DWORD PTR[0x0000]就是錯誤的,所以目標文件還要提供一個表,叫地址重定向表,address redirect table。

?

????總結一下:

????目標文件至少要提供三個表:未解決符號表,導出符號表和地址重定向表。

????未解決符號表:列出了本單元里有引用但是不在本單元定義的符號及其出現的地址。

????導出符號表:提供了本編譯單元具有定義,并且可以提供給其他編譯單元使用的符號及其在本單元中的地址。

????地址重定向表:提供了本編譯單元所有對自身地址的引用記錄。

?

????鏈接器的工作順序:

????當鏈接器進行鏈接的時候,首先決定各個目標文件在最終可執(zhí)行文件里的位置。然后訪問所有目標文件的地址重定義表,對其中記錄的地址進行重定向(加上一個偏移量,即該編譯單元在可執(zhí)行文件上的起始地址)。然后遍歷所有目標文件的未解決符號表,并且在所有的導出符號表里查找匹配的符號,并在未解決符號表中所記錄的位置上填寫實現地址。最后把所有的目標文件的內容寫在各自的位置上,再作一些另的工作,就生成一個可執(zhí)行文件。

????說明:實現鏈接的時候會更加復雜,一般實現的目標文件都會把數據,代碼分成好向個區(qū),重定向按區(qū)進行,但原理都是一樣的。

????明白了編譯器與鏈接器的工作原理后,對于一些鏈接錯誤就容易解決了。

?

????下面再看一看C/C++中提供的一些特性:

????extern:這就是告訴編譯器,這個變量或函數在別的編譯單元里定義了,也就是要把這個符號放到未解決符號表里面去(外部鏈接)。

?

????static:如果該關鍵字位于全局函數或者變量的聲明前面,表明該編譯單元不導出這個函數或變量,因些這個符號不能在別的編譯單元中使用(內部鏈接)。如果是static局部變量,則該變量的存儲方式和全局變量一樣,但是仍然不導出符號。

?

????默認鏈接屬性:對于函數和變量,默認鏈接是外部鏈接,對于const變量,默認內部鏈接。

外部鏈接的利弊:外部鏈接的符號在整個程序范圍內都是可以使用的,這就要求其他編譯單元不能導出相同的符號(不然就會報duplicated external symbols)。

內部鏈接的利弊:內部鏈接的符號不能在別的編譯單元中使用。但不同的編譯單元可以擁有同樣的名稱的符號。

?

????為什么頭文件里一般只可以有聲明不能有定義:頭文件可以被多個編譯單元包含,如果頭文件里面有定義的話,那么每個包含這頭文件的編譯單元都會對同一個符號進行定義,如果該符號為外部鏈接,則會導致duplicated external symbols鏈接錯誤。

?

????為什么公共使用的內聯函數要定義于頭文件里:因為編譯時編譯單元之間互不知道,如果內聯被定義于.cpp文件中,編譯其他使用該函數的編譯單元的時候沒有辦法找到函數的定義,因些無法對函數進行展開。所以如果內聯函數定義于.cpp里,那么就只有這個.cpp文件能使用它。

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯系該專欄作者,如若文章內容侵犯您的權益,請及時聯系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統(tǒng) 散熱

根據LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉