gcc編譯的四個階段,編譯後各段組成及記憶體空間分割槽

2021-10-03 21:16:56 字數 3834 閱讀 4959

1.預處理gcc -e ***.c -o ***.i

(1)巨集定義替換:#define pi 3.1415926。

(2)標頭檔案展開:搜尋#include中指定的標頭檔案,並將標頭檔案中的內容拷貝到原始檔中。

(3)注釋刪除等。

(4) #ifdef 等內容,完成條件編譯內容的替換。

注:此階段不檢查c程式**語法是否錯誤,生成.i檔案。

2.編譯gcc -s ***.i -o ***.s

(1)此階段檢查語法問題;

(2)然後生成中間彙編**,但是還不可執行,gcc編譯的中間檔案是[.s]檔案。

第乙個階段和第二個階段由gcc編譯器完成。

3.彙編gcc -c ***.s -o ***.o

(1)此階段主要完成將 彙編**.s 翻譯成 機器碼.o 指令,並將這些指令打包形成可重定位的目標檔案,[.o]檔案,是二進位制檔案。

(2)此階段由 as彙編器 完成。

4.鏈結gcc ***.o -o ***// -l -l[lib_name])

(1)此階段完成程式中呼叫的各種函式跟靜態庫和動態庫的鏈結,並將它們一起打包合併形成可執行檔案.elf。

(2)此階段由 ld鏈結器 完成。

注:(1)gcc編譯用到以下幾個程式:c編譯器gcc、彙編器as、鏈結器ld和二進位制轉換工具objcopy。

(2)nm:

(3)file,size

(4)objdump -d ***.elf > ***.asm

(5)objcopy binary

(1)linux下程式編譯後,可執行.elf檔案的記憶體布局(段指二進位制格式檔案中的一塊區域):

(1)文字段(.text):或稱**段,通常是用來存放程式執行**的一塊記憶體區域。這部分區域的大小在程式執行前就已經確定,並且記憶體區域通常屬於唯讀。某些架構也允許**段為可寫,即允許修改程式。在**段中,也有可能包含一些唯讀的常數變數,例如字串常量等。屬於cpu執行的機器指令部分( 存放函式體的二進位制** )。

(2)資料段(.data):通常是用來存放程式中已初始化的全域性變數的一塊記憶體區域。一般常量字串就是放在這裡的,程式結束後由系統釋放。唯讀資料段(唯讀常量區) 和資料段(全域性變數區)統稱為 資料段。

(3)bss段:未初始化的全域性變數和static修飾的區域性變數(初始化預設值為0);

注:1)對於data段,儲存的是初始化的全域性變數和stataic的區域性變數,直接載入記憶體即可。

2)text段儲存的是**直接載入。

3)bss段從目標檔案中讀取bss段大小,然後在記憶體中緊跟data段之後分配空間,並且清零(這也是為什麼全域性表量和static區域性變數不初始化會有0值得原因)。

(2)執行可執行檔案時,將可執行.elf檔案從磁碟上載入到記憶體中,並分配虛擬記憶體。linux(32位)下對映4g虛擬記憶體:使用者空間(0 - 3g) 和核心空間(3 - 4g)。

注:0xffff ffff :4g

0xc000 0000:3g

(3)使用者空間:動態儲存區和靜態儲存區

c語言中儲存區分類:

1)棧 :由編譯器自動分配釋放

2)堆 :一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os**

3)全域性區(靜態區):全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域(data區),未初始化的全域性變數和未初始化的靜態變數,static修飾的區域性變數在bss段。data和bss都屬於全域性區。

4)常量區(readonly):乙個專門放字串常量的地方。常量儲存區裡面的資料是放在**段裡的,不佔記憶體,是一塊比較特殊的儲存區,他們裡面存放的是常量,不允許修改。常量字串都存放在靜態儲存區,返回的是常量字串的首位址

c++中儲存區分類:

1)棧:就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變數的儲存區。裡面的變數通常是區域性變數、函式引數等。

2)堆:就是那些由new分配的記憶體塊,他們的釋放編譯器不去管,由我們的應用程式去控制,一般乙個new就要對應乙個delete。如果程式設計師沒有釋放掉,那麼在程式結束後,作業系統會自動**。

3)自由儲存區:就是那些由malloc等分配的記憶體塊,他和堆是十分相似的,不過它是用free來結束自己的生命的。

4)全域性/靜態儲存區:全域性變數和靜態變數被分配到同一塊記憶體中,在以前的c語言中,全域性變數又分為初始化的和未初始化的,在c++裡面沒有這個區分了,他們共同占用同一塊記憶體區。

5)常量儲存區:這是一塊比較特殊的儲存區,他們裡面存放的是常量,不允許修改(當然,你要通過非正當手段也可以修改)

(4)動態儲存區:棧(stack) 和 堆(heap),存放區域性變數。

a.棧區:執行**時,區域性變數只有其所在的函式呼叫時,區域性變數才由系統自動在棧上臨時開闢記憶體空間,區域性變數的生命週期隨其所在的函式的結束而被系統自動釋放記憶體空間。

b 堆區:malloc (標頭檔案),free§;p = null;

(5)靜態(全域性區)儲存區靜態區(全域性區)變數在編譯時就已經分配好記憶體空間:鏈結後的檔案未載入前還是存在磁碟上的,檔案中的變數、函式的位址都是邏輯位址。當程式執行,即載入可執行檔案到記憶體上時,會將邏輯位址對映到記憶體中,這時全域性變數就會被載入到記憶體中的資料段(data)。(6)備註:

(1)函式體中定義的變數通常是在棧上。

(2)用malloc, calloc, realloc等分配記憶體的函式分配得到的就是在堆上。

(3)在所有函式體外定義的是全域性變數,在全域性區。

(4)加了static修飾符後,不管變數在**都存放在全域性區(靜態區)

(5)在所有函式體外定義的static變數,表示只在該檔案中有效,不能被extern到別的檔案使用

(6)在函式體內定義的static表示只在該函式體內有效

(7)另外,函式中的"hello world"這樣的字串常量存放在 常量區。

(8)全域性變數和靜態static修飾的變數,沒有初始化會自動初始化為0。

(7)誤區:

1)普通全域性變數和靜態(static)全域性變數的區別

答:靜態全域性變數的作用域是整個源程式, 當乙個源程式由多個原始檔組成時,普通的全域性變數在各個原始檔中都是有效的,可以extern引用。 而靜態全域性變數則限制了其作用域, 即只在定義該變數的原始檔內有效, 在其它原始檔中不能使用它。

2)static 區域性變數和普通區域性變數的區別

答:區域性變數改變為靜態static區域性變數後,改變了它的儲存方式即改變了它的生命週期。

static 區域性變數只被初始化一次,下一次是依據上一次值。

3)static 函式與普通函式的區別

答:a)static 函式與普通函式作用域不同,static函式僅作用在本檔案。

b)只在當前原始檔中使用的函式應該說明為內部函式(static),內部函式應該在當前原始檔中說明和定義。

c)對於可在當前原始檔以外使用的函式,應該在乙個標頭檔案中說明,要使用這些函式的原始檔要包含這個標頭檔案。

d)static 函式在記憶體中只有乙份(.data),普通函式在每個被呼叫中維持乙份拷貝。

e)static修飾的函式

gcc編譯的四個階段

gcc編譯的四個階段 如下圖 e preprocess only do not compile,assemble or link 只預處理,不會編譯 彙編 鏈結 s compile only do not assemble or link 只編譯,不會彙編 鏈結 c compile and asse...

GCC編譯的四個階段

gcc編譯流程分為4個步驟,分別為 預處理 pre processing 編譯 compiling 彙編 assembling 鏈結 linking 1 預處理階段 在該階段,編譯器將上述 中的stdio.h編譯進來,並且使用者可以使用gcc的選項 e 進行檢視,該選項的作用是把源 進行預處理。預處...

程式編譯的四個階段

預處理將標頭檔案展開,將巨集定義替換,生成符號檔案.s 編譯則包含了詞法檢查,語法檢查,許可權檢查,優化 組裝 將編譯後的 組裝成機器碼,形成位置無關的目標檔案 o 鏈結將多個位置無關的目標檔案合併成可執行檔案 可見組裝才是平台相關的,之前的操作都與平台無關,換句話說是編譯前端和編譯後端 乙個類的成...