C C 的編譯與執行

2021-06-16 08:54:34 字數 3465 閱讀 7485

c/c++編譯前,首先要對源**執行預處理。預處理器(preprocessor)是乙個簡單的程式,它用程式設計師(利用預處理器指令)定義好的模式代替源**中的模式(刪除注釋、包含其他檔案以及執行巨集),預處理後生成中間檔案.i(文字)。接下來對於.i檔案進行語法分析。編譯器把源**分解成小的單元並把它們按樹形結構組織起來。表示式中「a + b」中的「a」、「+」和「b」就是語法分析樹的葉子節點。語法分析樹建立後有時會根據使用者定義,使用全域性優化器(global optimizer)來生成更短、更快的**。

全域性優化器主要是進行以下優化:

優代完成之後,還需要使用**生成器(code generator)遍歷語法分析樹,把樹的每個節點轉化成組合語言,這個期間儲存為中間檔案.s(組合語言 文字)。之後根據使用者定義可以使用窺孔優化器(peephole optimizer)從相鄰一段**中查詢冗餘彙編語句。

窺孔優化,顧名思義,是一種很區域性的優化方式,編譯器僅僅在乙個基本塊或者多個基本塊中,針對已經生成的**,結合cpu自己指令的特點,通過一些認為可能帶來效能提公升的轉換規則,或者通過整體的分       析,通過指令轉換,提公升**效能。別看這些**轉換很區域性,很小,但可能會帶來很大的效能提公升。

這個窺孔,你可以認為是乙個滑動視窗,編譯器在實施窺孔優化時,就僅僅分析這個視窗內的指令。每次轉換之後,  可能還會暴露相鄰視窗之間的某些優化機會,所以可以多次呼叫窺孔優化,盡可能提公升效能。

窺孔優化可以在四個方面尋找優化機會:冗餘指令刪除,包括冗餘的load和store指令以及死**(不會執行的**);控制流優   化;強度削弱;利用特有指令。

接下來使用彙編器將彙編原始檔翻譯成對應的機器指令,而且還寫入一些東西與機器指令打包成可重新定位目標程式格式的檔案,生成中間檔案.o(目標檔案 二進位制)。最後使用聯結器(linker)把一組目標模組連線成為乙個可執行程式(最終檔案),簡單的講,把目標的庫檔案和所需要的引用的靜、動態鏈結庫進行鏈結,即需要把其他靜態庫合成到可執行檔案中,轉換相應的符號引用為位址,然後確保所引用的其他動態鏈結庫的符號存在。此外,鏈結器還要完成程式中各目標檔案的位址空間的組織,這可能設計重定位工作。大多數現代作業系統都提供靜態鏈結和動態鏈結兩種形式。

注:靜態型別檢查是在建立語法分析樹時完成的。

windows程式的啟動過程

4g的虛擬記憶體中,使用者程序可以占有2gb的私有位址空間;作業系統占有剩餘的2gb空間。在32位x86系統中: 從0x00000000到0x7fffffff的空間中存放著 應用程式**,全域性變數,每個執行緒堆疊,dll**。

從0x80000000到0xc0000000的空間中存放著 核心和執行體,hal(硬體抽象層),引導驅動程式。

從0xc0000000到0xc0800000的空間中存放著 程序頁表和超空間。

從0xc0800000到0xffffffff的空間中存放著 系統快取記憶體,分頁緩衝池,非分頁緩衝池。

createprocess開啟應用程式檔案,它先掃瞄該檔案的檔案頭,該檔案頭里含有檔案能執行在哪個環境之下,如果是win32環境,系統就直接載入檔案的**和資料並輸入(import)該檔案執行所需的動態鏈結庫。如果不是win32環境比如時os/2的.exe則先載入相應的環境子系統,載由該環境載入該檔案的**和資料以及該檔案執行所需的動態鏈結庫。具體載入動態鏈結庫過程如下

載入器(loader)讀入可執行程式的匯入符號表,根據這些符號表可以查詢出該可執行程式的所有依賴的動態鏈結庫。

載入器針對該程式的每乙個動態鏈結庫呼叫loadlibrary (1)查詢對應的動態庫檔案,載入器為該動態鏈結庫確定乙個合適的基位址。如果該基位址和動態鏈結庫希望記載的基位址不同,載入器還要為該庫做rebase,然後把整個動態鏈結庫對映到程序的虛擬記憶體空間中。

(2)載入器讀取該動態鏈結庫的匯入符號表和匯出符號表,比較應用程式要求的匯入符號是否匹配該庫的匯出符號。

(3)針對該庫的匯入符號表,查詢對應的依賴的動態鏈結庫,如有跳轉,則跳到第三步

(4)呼叫該動態鏈結庫的初始化函式

程序載入**和資料完畢後,就開始建立執行緒來執行程序空間內的**。程序是靜態的,它只是執行緒的容器。乙個程序至少因該有乙個執行緒(main thread),其它執行緒都是主線程通過呼叫createthread函式建立的。執行緒也是核心物件,他的實際建立者是乙個叫ntcreatethread的windowsnt系統服務函式。乙個執行緒其實只是乙個執行緒核心物件和兩個堆疊(乙個核

心堆疊,用於執行緒執行在核心態;乙個使用者堆疊,用於執行緒執行在使用者態),執行緒與程序類似,也擁有執行緒核心物件計數和執行緒控制代碼,這裡不詳述。執行緒用於描述程序中的執行路徑。每當程序被初始化時,系統就要建立乙個主線程。該執行緒與c/c++執行時庫的啟動**一道開始執行,啟動**則呼叫進入點函式(就是我們的main函式,它也是主線程的進入點函式),並且繼續執行直到進入點函式返回並且c/c++執行時庫的啟動**呼叫exitprocess為止。每個執行緒函式必須有乙個返回值,它將作為執行緒的退出**。對於主線程來說,這個返回值將傳給c/c++執行時庫的啟動函式。

c/c++執行時庫的啟動函式其實是乙個程式的真正呼叫的第乙個函式,它是在程式鏈結時由鏈結程式選擇相應的啟動函式並加到程式的開始處。c/c++執行時庫有四個版本的啟動函式,他們分別對應不同型別的應用程式。比如: 需要ansi字元和字串的gui應用程式的啟動函式是winmaincrtstartup,其對應的進入點函式是winmain

需要unicode字元和字串的gui應用程式的啟動函式是wwinmaincrtstartup,其對應的進入點函式是wwinmain

需要ansi字元和字串的cui應用程式(如控制台console程式)的應用程式的啟動函式是maincrtstartup,對應的入口點函式為main

需要unicode字元和字串的cui應用程式(如控制台console程式)的應用程式的啟動函式為wmaincrtstartup,對應的入口點函式為wmain

c/c++執行時庫的啟動函式的功能如下(以wwinmaincrtstartup為例):

*檢索指向新程序的完整命令列指標;

*檢索指向新程序的環境變數的指標;

*對c/c++執行時的全域性變數進行初始化;

*對c執行期的記憶體單元分配函式(比如malloc,calloc)和其他低層i/o例程使用的記憶體棧進行初始化。

*為c++的全域性和靜態類呼叫建構函式。

當這些初始化工作完成後,該啟動函式就呼叫wwinmain函式進入應用程式的執行。

當wwinmain函式執行完畢返回時,wwinmaincrtstartup啟動函式就呼叫c執行期的exit()函式,將返回值(nmainretval)傳遞給它。之後exit()便開始收尾工作:

*呼叫由_onexit()函式呼叫和註冊的任何函式。

*為c++的全域性和靜態類呼叫析構函式;

*呼叫作業系統的exitprocess函式,將nmainretval傳遞給它,這使得作業系統能夠撤銷程序並設定它的exit()**。

以上就是windows 環境下可執行程式的啟動過程

linux程式啟動過程

本文**   

C C 的編譯與執行

c c 編譯前,首先要對源 執行預處理。預處理器 preprocessor 是乙個簡單的程式,它用程式設計師 利用預處理器指令 定義好的模式代替源 中的模式 刪除注釋 包含其他檔案以及執行巨集 預處理後生成中間檔案.i 文字 接下來對於.i檔案進行語法分析。編譯器把源 分解成小的單元並把它們按樹形結...

linux編譯執行C C 程式

在linux系統下編譯執行c c 程式。編譯c程式使用gcc,編譯c 程式使用g ubuntu14.04預設安裝了gcc,但是沒有安裝g 1.首先確定已經安裝gcc g 使用whereis gcc或者whereis g 檢視是否安裝gcc g 命令如下 liu liu whereis g 如果安裝,...

VScode配置,編譯,執行C C

第一步 預設安裝就行,可以更改一下安裝路徑 第二步 安裝必須的軟體和便於開發常用的外掛程式 第三步 什麼是 mingw w64 mingw 的全稱是 minimalist gnu on windows 它實際上是將經典的開源 c語言 編譯器 gcc 移植到了 windows 平台下,並且包含了 wi...