一、編譯過程:
第一步,預編譯,展開標頭檔案,進行巨集替換。
第二步,編譯,**優化(gcc -o1234),符號彙總。
第三步,彙編,根據對應關係,將彙編指令轉換為本地作業系統的機器碼。
第四步,生成可重定位的目標檔案(沒有位址),可重定位的目標檔案中包含符號表;
第一步,將所有的目標檔案的段進行合併,其中包括合併符號表,進行符號解析,解析正確,給符號表的符號分配虛擬位址。
第二步,將**段的指令進行符合重定向。
程序在記憶體上的布局:
例子:
兩個檔案的符號表:
在x86體系32位linux核心/作業系統下:
每乙個程序在執行的時候,系統會為其分配乙個以上構造的4g的虛擬位址空間:3g為使用者空間(私有)1g為核心空間(共享)對任何乙個普通程序,涉及到5種不同段。
**段:用來存放程式的執行**,大小在程式執行前已經確定,並且唯讀不可寫,也包含一些唯讀的常數變數,例字串常量等。
資料段:用來存放程式中已初始化的全域性變數(靜態記憶體分配)。
bss段:用來存放程式中未初始化的或者初始化為零的全部變數(靜態記憶體分配)。
堆區:用於存放程序執行中被動態分配的記憶體段,大小不固定,可動態擴張或縮減。當程序呼叫malloc等函式分配記憶體,新分配 的記憶體被動態新增到堆當利用free等函式釋放記憶體時,記憶體從堆中被剔除。
棧區:用於存放程式臨時建立的區域性變數,即函式括弧「{}」中定義的變數。函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且呼叫結束後,函式返回值也會被存放回棧中。(由作業系統分配,記憶體的申請和**都有os管理)
全域性的未初始化變數存在於.bss段中,具體表現為乙個佔位符;
全域性的已初始化變數存在與.data段中;
函式內的自動變數都在棧上分配空間;
.bss不占用.exe檔案空間,內容由作業系統初始化(清零),.data需要占用,內容由程式初始化;
bss段:不給該段的資料分配空間,只是記錄資料所需空間的大小。
data段:則資料分配空間,資料儲存在目標檔案中。
重定位就是把程式的邏輯位址空間變換成記憶體中的實際實體地址空間的過程,也就是說在裝入時對目標程式中指令和資料的修改過程。
重定位分為動態重定位和靜態重定位。
靜態重定位:在程式裝入記憶體的過程中完成,在程式開始執行前,程式中的各個位址有關的項均已完成重定位,位址變換通常是在裝入時一次完成的,以後不再改變。
動態重定位:它不是在程式裝入記憶體時完成的,而是cpu每次訪問記憶體時 由動態位址變換機構(硬體)自動進行把相對位址轉換為絕對位址。動態重定位需要軟體和硬體相互配合完成。
因為.o檔案下符號表裡的符號未分配位址,所以執行的時候因為找不到儲存位址而失敗。
1.什麼是符號解析?
給未定義的符號,找到其定義的地方。
2.什麼是符號重定向?
給符號表的符號分配虛擬位址後,將**段的符號分配虛擬位址。
3.符號為什麼不能直接分配物理記憶體?
防止實體地址提前分配後,被其它記憶體占用,以及分配位址衝突,並未知程序執行的時間,產生記憶體占用。
4.程式執行完成後,物理記憶體上的位址會釋放嗎?
動態申請的記憶體,不會被釋放,除非使用free釋放函式,否則會造成記憶體洩漏。但實則系統會自動**,包括洩露的部分
const常量是由編譯器處理的,提供型別檢查和作用域檢查
巨集定義由預處理器處理,單純的文字替換
typedef發生在編譯階段
C 編譯器的函式編譯流程
c 中的型別查詢過程相對簡單,基本上就是名字查詢,這裡不再介紹。對於 cpp 檔案中呼叫的乙個函式 或成員函式 編譯器主要做了下面三件事情 1 名字查詢 先在所在編譯單元中可見名字實體中進行名字查詢 1 類成員函式優先 物件所在的類 基類 一 經找到就停止查詢 2 如果沒有 在相應的名字空間中做進一...
C 編譯器的函式編譯流程
c 中的型別查詢過程相對簡單,基本上就是名字查詢,這裡不再介紹。對於 cpp 檔案中呼叫的乙個函式 成員函式 編譯器主要做了下面三件事情 1 名字查詢 先在所在編譯單元中可見名字實體中進行名字查詢 1 類成員函式優先 物件所在的類 基類 一經找到就停止查詢 如果沒有 2 在相應的名字空間中做進一步的...
uboot配置 編譯 工作流程
準備 在uboot目錄下開啟makefile,比如我用的是2440,則找到mini2440 config配置項 若是直接複製別人的,則可能回找不到 配置 在uboot目錄下輸入make mini2440 config,結果如下 make mini2440 config 編譯uboot 在uboot目...