天天看點

C++ - 編譯器工作原理

文章目錄

      • 1. 預處理
        • 1.1 頭檔案包含
        • 1.2 宏替換
        • 1.3 條件編譯
      • 2. 編譯
      • 3. 彙編
      • 4. 連結
      • g++(gcc)編譯選項

編譯器可以閱讀以某一種語言(源語言)編寫的程式,并把該程式翻譯成一個等價的、用另一種語言(目智語言)編寫的程式。

C++編譯系統将一個程式轉化為可執行程式的過程包含:

1. 預處理

預處理器是在程式源檔案被編譯之前根據預處理指令對程式源檔案進行處理的程式。

以#号開頭辨別,末尾不包含分号。

預處理指令不是C/C++語言本身的組成部分,不能直接對它們進行編譯和連結。

C/C++提供的預處理功能主要有檔案包含、宏替換、條件編譯等。

1.1 頭檔案包含

  • 頭檔案是一種文本檔案,以擴充名.h(.hpp)儲存。
  • 頭檔案中一般放一些重複使用的代碼,例如函數聲明、變量聲明、常數定義、宏的定義等等。
  • 當使用預處理指令#include引用頭檔案時,相當于将頭檔案中所有内容,複制到include處。
#include <xxx.h>    // 直接到系統目錄中去找某些頭檔案。
#include "xxx.h"    // 先到源檔案所在檔案夾去找,然後再到系統目錄中去找某些頭檔案。
           

1.2 宏替換

  • 一般用一個短的名字代表一個長的代碼序列。
  • 宏定義包括:無參數宏定義和帶參數宏定義
  • 宏名和宏參數所代表的代碼序列可以是任何意義的内容,如類型、常量、變量、操作符、表達式、語句、函數、代碼塊等。
  • 宏替換首先将源檔案中在宏定義随後所有出現的宏名均用其所代表的代碼序列替換之,如果是帶參數宏則接着将代碼序列中的宏形參名替換為宏實參名。
  • 宏替換隻作代碼字元序列的替換工作,不作任何文法的檢查,也不作任何的中間計算,一切其它操作都要在替換完後才能進行。如果宏定義不當,錯誤要到預處理之後的編譯階段才能發現。
  • 如果一個宏定義中代碼序列太長,一行不夠時,可續行。續行是在鍵入回車符之前先鍵入符号\,注意回車要緊接在符号\之後,中間不能插入其它符号,當然代碼序列最後一行結束時不能有\。

1.3 條件編譯

  • 一般情況下,在進行編譯時對源程式中的每一行都要編譯,但是有時希望程式中某一部分内容隻在滿足一定條件時才進行編譯,如果不滿足這個條件,就不編譯這部分内容,這就是條件編譯。
  • 條件編譯主要是進行編譯時進行有選擇的挑選,注釋掉一些指定的代碼,以達到多個版本控制、防止對檔案重複包含的功能。if, #ifndef, #ifdef, #else, #elif, #endif是比較常見條件編譯預處理指令,可根據表達式的值或某個特定宏是否被定義來确定編譯條件。
  • 此外,還有 #pragma 指令,它的作用是設定編譯器的狀态或訓示編譯器完成一些特定的動作。

2. 編譯

通過詞法分析和文法分析(與形式語言和自動機有關),在确認所有指令都是符合文法規則之後,将其翻譯成等價的中間代碼表示或彙編代碼。

3. 彙編

對于被翻譯系統處理的每一個C/C++語言源程式,都将最終經過這一處理而得到相應的目标檔案。目标檔案中所存放的也就是與源程式等效的目标機器語言代碼。目标檔案由段組成,通常一個目标檔案中至少有兩個段:代碼段和資料段。

  • 代碼段:該段中所包含的主要是程式的指令。該段一般是可讀和可執行的,但一般卻不可寫。
  • 資料段:主要存放程式中要用到的各種全局變量或靜态的資料。一般資料段都是可讀,可寫,可執行的。

4. 連結

連結程式的主要工作就是将有關的目标檔案彼此相連接配接,也即将在一個檔案中引用的符号同該符号在另外一個檔案中的定義連接配接起來,使得所有的這些目标檔案成為一個能夠按作業系統裝入執行的統一整體。主要有靜态連結和動态連結兩種方式:

  • 靜态連結:在連結階段,會将彙編生成的目标檔案.o與引用到的庫一起連結打包到可執行檔案中,程式運作的時候不再需要靜态庫檔案。
  • 動态連結:把調用的函數所在檔案子產品(DLL)和調用函數在檔案中的位置等資訊連結進目标程式,程式運作的時候再從DLL中尋找相應函數代碼,是以需要相應DLL檔案的支援。

這裡的庫是寫好的現有的,成熟的,可以複用的代碼。現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,是以庫的存在意義非同尋常。本質上來說庫是一種可執行代碼的二進制形式,可以被作業系統載入記憶體執行。庫有兩種:靜态庫(.a、.lib)和動态庫(.so、.dll),所謂靜态、動态是指連結方式的不同。要注意靜态連結庫中不能再包含其他的動态連結庫或者靜态庫,而在動态連結庫中還可以再包含其他的動态或靜态連結庫。

g++(gcc)編譯選項

  • -shared :指定生成動态連結庫。
  • -static :指定生成靜态連結庫。
  • -fPIC :表示編譯為位置獨立的代碼,用于編譯共享庫。目标檔案需要建立成位置無關碼, 念上就是在可執行程式裝載它們的時候,它們可以放在可執行程式的記憶體裡的任何地方。
  • -L. :表示要連接配接的庫所在的目錄。
  • -l:指定連結時需要的動态庫。編譯器查找動态連接配接庫時有隐含的命名規則,即在給出的名字前面加上lib,後面加上.a/.so來确定庫的名稱。
  • -Wall :生成所有警告資訊。
  • -ggdb :此選項将盡可能的生成gdb 的可以使用的調試資訊。
  • -g :編譯器在編譯的時候産生調試資訊。
  • -c :隻激活預處理、編譯和彙編,也就是把程式做成目标檔案(.o檔案) 。
  • -Wl,options :把參數(options)傳遞給連結器ld 。如果options 中間有逗号,就将options分成多個選項,然後傳遞給連結程式。
  • -S:隻激活預處理和編譯,就是指把檔案編譯成為彙編代碼。例子用法 gcc -S hello.c他将生成.s的彙編代碼,你可以用文本編輯器察看

繼續閱讀