C 編譯 執行原理

2021-06-10 07:30:16 字數 2957 閱讀 7772

關於編譯與記憶體的關係,以及執行時記憶體的劃分

1、所謂在編譯期間分配空間指的是靜態分配空間(相對於用new動態申請空間),如全域性變數或靜態變數(包括一些複雜型別的

常量),它們所需要的空間大小可以明確計算出來,並且不會再改變,因此它們可以直接存放在可執行檔案的特定的節裡(而且

包含初始化的值),程式執行時也是直接將這個節載入到特定的段中,不必在程式執行期間用額外的**來產生這些變數。

其實在執行期間再看「變數」這個概念就不再具備編譯期間那麼多的屬性了(諸如名稱,型別,作用域,生存期等等),對應的

只是一塊記憶體(只有首址和大小), 所以在執行期間動態申請的空間,是需要額外的**維護,以確保不同變數不會混用記憶體。

比如寫new表示有一塊記憶體已經被占用了,其它變數就不能再用它了; 寫delete表示這塊記憶體自由了,可以被其它變數使用了。

(通常我們都是通過變數來使用記憶體的,就編碼而言變數是給記憶體塊起了個名字,用以區分彼此)

記憶體申請和釋放時機很重要,過早會丟失資料,過遲會耗費記憶體。特定情況下編譯器可以幫我們完成這項複雜的工作(增加額外

的**維護記憶體空間,實現申請和釋放)。從這個意義上講,區域性自動變數也是由編譯器負責分配空間的。進一步講,記憶體管理

用到了我們常常掛在嘴邊的堆和棧這兩種資料結構。

最後對於「編譯器分配空間」這種不嚴謹的說法,你可以理解成編譯期間它為你規劃好了這些變數的記憶體使用方案,這個方案寫

到可執行檔案裡面了(該檔案中包含若干並非出自你大腦衍生的**),直到程式執行時才真正拿出來執行。

2、編譯其實只是乙個掃瞄過程,進行詞法語法檢查,**優化而已。我想你說的「編譯時分配記憶體」是指「編譯時賦初值」,它只是形成乙個文字,檢查無錯誤,並沒有分配記憶體空間。

當你執行時,系統才把程式匯入記憶體。乙個程序(即執行中的程式)在主要包括以下五個分割槽:

棧區、堆區、全域性資料區/靜態區、**區、常量區 

堆區用來存放程式中動態申請記憶體的變數

全域性變數/靜態區用來存放程式中的全域性變數或者是靜態變數,因為它們的大小是確定的,在編譯期間就已經進行靜態空間的分配,而且不會改變,這樣會提高程式對這些資料的訪問速度

**區(code)用來存放編譯後的二進位制**

常量區用來存放我們宣告的常量(const型別)

**(編譯後的二進位制**)放在code區,**中生成的各種變數、常量按不同型別分別存放在其它四個區。系統依照**順序

執行,然後依照**方案改變或呼叫資料,這就是乙個程式的執行過程。

3、編譯時分配記憶體

---------------

編譯時是不分配記憶體的。此時只是根據宣告時的型別進行佔位,到以後程式執行時分配記憶體才會正確。所以宣告是給編譯器看的

,聰明的編譯器能根據宣告幫你識別錯誤。

執行時分配記憶體

---------------

這是對的,執行時程式是必須調到「記憶體」的。因為cpu(其中有多個暫存器)只與記憶體打交道的。程式在進入實際記憶體之前要首

先分配物理記憶體。

編譯過程

---------------

當執行這個exe檔案以後,此程式就被載入到記憶體中,成為程序。此時一開始程式會初始化一些全域性物件,然後找到入口函式

,就開始按程式的執行語句開始執行。此時需要的記憶體只能在程式的堆上進行動態增加/釋放了。

編譯過程:是把原始檔翻譯成目標檔案存到硬碟上,這個過程佔不佔記憶體沒關係,是編譯器做的事,目標檔案由三個部分組成:

1. 檔案資訊,包括檔案型別,檔案大小等等,如:dll檔案的前兩個位元組是0x4d 0x5a。

2. **,就是程式,如 hdata data = new hdata(); hdata是自己定義的類,這一句被轉換成如下形式,共佔37個位元組。

00000040 b9 10 7f da 00 mov ecx,0da7f10h  

00000045 e8 e2 4d d4 fb call fbd44e2c  

0000004a 89 45 b4 mov dword ptr [ebp-4ch],eax  

0000004d 8b 4d b4 mov ecx,dword ptr [ebp-4ch]  

00000050 e8 0b f0 ab fb call fbabf060  

00000055 8b 55 c4 mov edx,dword ptr [ebp-3ch]  

00000058 8b 45 b4 mov eax,dword ptr [ebp-4ch]  

0000005b 8d 92 84 01 00 00 lea edx,[edx+00000184h]  

00000061 e8 3a 5b fe 74 call 74fe5ba0

3. 資料,包括全域性變數、靜態變數和常量。類成員變數、方法區域性變數不編譯到檔案中。如:static int a = 0; 在檔案中佔四個位元組,int a = 1, 在檔案中不佔位元組,string str = "12345", 雖然是類成員,但其中隱含常量「12345」,在檔案中佔5個位元組 。

執行過程:

1. 雙擊圖示時,系統把exe檔案全部調入記憶體,主要包括所有程式和全域性變數,這一部分記憶體一直被占用到退出程式。

2. 執行程式,還以這一句為例,hdata data = new hdata(); hdata類的程式已經在記憶體中,所有hdata類的例項共用一套程式,系統只是為hdata的資料(主要是hdata中的變數)分配一塊記憶體,並把這塊記憶體的起點,靜態變數除外,它在載入exe檔案的時候調入記憶體。data 失效的時候,這一塊記憶體被釋放。

區域性變數,void aaaa() 這段程式的主體部分大約佔5個位元組,a變數不佔記憶體,呼叫aaaa()的時候執行一行彙編碼 add bp, 4 就是在棧中分配四個位元組給a,aaaa()返回的時候,a變數占用的四個位元組被釋放。

3. 退出應該程式的時候釋放exe檔案占用的記憶體。

以上只是乙個大至的原理,實際情況要複雜的多,象分配的記憶體是可移動的,甚至會被放到記憶體中。不過咱們做應用程式的了解這些就足夠了。再深入是的做系統和做編譯器的人管的事。

寫完以後才發現是07年的帖子,哈,還是發了吧。

...

C 編譯鏈結執行原理

1.預編譯 生成.i檔案 1.將所有的 define 刪除,並且展開所有巨集 2.處理掉所有條件預編譯指令,如 if ifdef elif else endif 3.處理 include 指令,這是乙個遞迴過程 4.刪除所有的注釋 和 5.新增行號和檔名標識 6.保留所有的 pragma編譯器指令,...

編譯原理之解釋執行

原始碼 生成目標 後就可進行解釋執行了。整體思路 1.具體實現 掃瞄目標 表,根據每條 在的作用進行相應的實現。在這個實驗中,我用了乙個int陣列來記錄活動,用了乙個棧來進行計算。有著指向 的指標sp,當sp的值指向最後一條 時則結束。每條 的具體實現 1.1 jmp 直接將 指標更改為a域裡的值,...

編譯原理 執行儲存分配

執行時記憶體的劃分 各區段功能說明 1 程式 區 存放函式體的二進位制 2 全域性區 靜態區 static 全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。程式結束後由系統釋放。3 文字常量區 常量字串就是放...