基本介紹
- 中文名:編譯單元
- 外文名:translate unit
- 適用領域:程式、代碼
- 相關概念:源檔案、預編譯、編譯器
C++中的編譯單元,Java中的編譯單元,在多個編譯單元中如何定義常量(C++),
C++中的編譯單元
編譯單元,指的是代碼的物理組織形式。根據C++標準,每一個cpp 檔案就是一個編譯單元。
- 編譯器不會去編譯 `.h` 或者 `.hpp` 檔案;
- 編譯器只會編譯 `.c` 或 `.cpp` 檔案;
簡單來說,當一個c或cpp檔案在編譯時,預處理器首先遞歸包含頭檔案,這也就是為什麼常會有:#ifndef……#define……#endif。之後,形成一個含有所有必要信息的單個源檔案,這個源檔案就是一個編譯單元。這個編譯單元會被編譯成為一個與cpp檔案名稱同名的目標檔案 。
編譯器不能檢查跨越目標檔案或編譯單元之間的名稱衝突,這是連結器的工作。連結器把不同編譯單元中產生的符號聯繫起來,構成一個可執行程式。如:
//檔案first.cpp int integerValue = 0; int main(){ int integerValue = 0; return 0; }; //檔案second.cpp int integerValue = 0; /* 錯誤: error LNK2005: "int integerValue" (?integerValue@@3HA) 已經在 second.obj 中定義 first.obj */
GCC將C++代碼轉為機器碼,理論上需要四個步驟:預處理(preprocessing)、編譯(compilation)、彙編(assembly)以及連結(linking)3;四個步驟對應四個主體:預處理器(preprocessor)、編譯器(compiler)、彙編器(assembler)以及連結器(linker)。實際預處理與編譯其實是一個步驟,共需要三個步驟:預處理&編譯、彙編以及連結。
Java中的編譯單元
當編寫一個Java原始碼檔案時,此檔案以.java結尾,被稱為編譯單元。
1、 編譯單元中可以有一個public類,且只能有一個public類,作為外界訪問該類的接口,該類的名稱必須與檔案名稱稱一樣。
2、 編譯單元中可以沒有public類,但必須有一個類名稱與檔案名稱稱相同。
3、 編譯單元中可以有一些額外的類,這些類在包訪問許可權的。
代碼組織
1、當編譯(javac)一個.java檔案時,在該編譯單元(即.java檔案)中的每個類都會有一個輸出檔案.class檔案,每個輸出檔案的名稱與.java檔案中每個類的名稱相同。
2、Java可運行程式是一組可以打包並壓縮為一個Java文檔檔案(JAR檔案)的.class檔案。Java解釋器負責這些檔案的查找、裝載和解釋。
Java解釋器運行過程
1、 找出環境變數CLASSPATH(通過作業系統設定,也可不用設定,一般編譯環境會為你設定),CLASSPATH包含一個或多個目錄,用來查找.class檔案的根目錄。
2、 從根目錄開始,解釋器獲取包的名稱並將每個句點替換成\
3、 得到的路徑與CLASSPATH中的各個不同的項相連線,解釋器就在這些目錄中查找與你創建的類名稱相關的.class檔案。
在多個編譯單元中如何定義常量(C++)
【方法一】: 在某個公用的頭檔案中直接在某個名字空間中或者全局名字空間中定義符號常量並初始化(有無static)無所謂,例如:
// CommonDef.h
const int MAX_LENGTH=1024;
然後每一個使用它的編譯單元#include改頭檔案即可
const int MAX_LENGTH=1024;
然後每一個使用它的編譯單元#include改頭檔案即可
【方法二】: 在某個公用頭檔案中並且在某個名字空間中或者全局名字空間中將符號常量聲明為extern的,例如:
//CommonDef.h
extern const int MAX_LENGTH;
並且在某個源檔案中定義一次並初始化:
const int MAX_LENGTH=1024;
然後每一個使用它的編譯單元#include上述頭檔案即可。
//CommonDef.h
extern const int MAX_LENGTH;
並且在某個源檔案中定義一次並初始化:
const int MAX_LENGTH=1024;
然後每一個使用它的編譯單元#include上述頭檔案即可。
兩種方法的比較
優點:
方法一:
維護方便
方法二:
(1)節約存儲,每一個編譯單元訪問都是這個唯一的定義。
(2)修改初值後只需重新編譯定義所在編譯單元即可,影響面很小。
(2)修改初值後只需重新編譯定義所在編譯單元即可,影響面很小。
缺點:
方法一:
(1)如果修改常量初值,則將影響多個編譯單元,所有受影響的編譯單元必須重新編譯。
(2)每一個符號常量在每一個包含了它們的編譯單元內都存在一份獨立的拷貝內容,每個編譯單元訪問的就是各自的拷貝內容,因此浪費存儲空間。
方法二:
如果要改變初值,要改變源檔案。