編譯器的工做過程

簡單的說,其實要理解cpp文件與頭文件有什麼不一樣之處,首先須要弄明白編譯器的工做過程,通常說來編譯器會作如下幾個過程:函數

1.預處理階段(也就是常說的切token)spa

2.詞法與語法分析階段code

3.編譯階段首先編譯成純彙編語句,再將之彙編成跟CPU相關的二進制碼,生成各個目標文件 (.obj文件)blog

4.鏈接階段將各個目標文件中的各段代碼進行絕對地址定位,生成跟特定平臺相關的可執行文件,固然,最後還能夠用objcopy生成純二進制碼,也就是去掉了文件格式信息。(生成.exe文件)token

編譯器在編譯時是以cpp文件爲單位進行的也就是說若是你的項目中一個cpp文件都沒有,那麼你的項目將沒法編譯。鏈接器是以目標文件爲單位,它將一個或多個目標文件進行函數與變量的重定位(肯定每一個函數和變量相對於程序起始位置的地址,這裏還不是真正的內存的地址,真正內存的地址要等到程序載入器根據某個寄存器的地址肯定),生成最終的可執行文件。在PC上的程序開發,通常都有一個main函數,這是各個編譯器的約定。固然,你若是本身寫鏈接器腳本的話,能夠不用main函數做爲程序入口!!!!內存

  (main .c文件  目標文件  可執行文件)開發

有了這些基礎知識,再言歸正傳,爲了生成一個最終的可執行文件,就須要一些目標文件,也就是須要cpp文件,而這些cpp文件中又須要一個main函數做爲可執行程序的入口,那麼咱們就從一個cpp文件入手,假定這個cpp文件內容以下:編譯器

main.c函數io

#include <stdio.h> #include "mytest.h"

int main(int argc,char **argv) { test = 25; printf("test.................%d\n",test); return 0; }

mytest.h頭文件內容以下:編譯

int test;

如今以這個例子來說解編譯器的工做:

1.預處理階段:編譯器以cpp文件做爲一個單元,首先讀這個cpp文件,發現第一句與第二句包含一個頭文件,就會在全部搜索路徑中尋找這兩個頭文件,找到以後,就會到相應頭文件中再去處理宏、變量、函數聲明、嵌套的頭文件等,檢測依賴關係,進行宏替換,看是否有重複定義與聲明的狀況發生,最後將那些文件中全部的東東所有掃描進這個當前的cpp文件中,造成一箇中間"cpp文件"。

在這一步中至關於將那個頭文件中的test變量掃描進了一箇中間cpp文件,那麼test變量就變成了這個文件中的一個全局變量。在stdio.h這個頭文件中有一些函數的聲明,這時也把這些函數的聲明一股腦的掃描到了這個中間cpp文件中(只是掃描了函數的聲明,並無實現)。

2.編譯階段:此時就爲這個中間cpp文件的全部變量、函數形參分配空間(原則上,在這裏只能看到.h文件中函數、變量的聲明,爲變量和函數的形參等分配空間),將各個函數編譯成二進制碼,按照特定目標文件格式生成目標文件,在這種格式的目標文件中進行各個全局變量、函數的符號描述(編譯器維護一個符號描述表),將這些二進制碼按照必定的標準組織成一個目標文件。

此時的每個cpp文件都被編譯器編譯成了一個目標文件,同時每一個目標文件都有一張符號表,這張符號表中記錄了這個cpp文件都用到了哪些變量,哪些函數,函數的參數是什麼類型的,有幾個參數。同時爲變量和函數形參開闢了內存空間。因此這裏編譯器把你這個cpp用到的全部的東西都記錄下來了,若是有重複定義或者沒有定義的變量、函數等,編譯器一會兒就知道了。

3.鏈接階段:將上一步成生的各個目標文件,根據一些參數,鏈接生成最終的可執行文件,主要的工做就是重定位各個目標文件的函數、變量等,至關於將個目標文件中的二進制碼按必定的規範合到一個文件中。

相關文章
相關標籤/搜索