文章目錄
引言
從源碼到可執行程序,經過了編譯、鏈接這兩個過程
- 編譯:將用戶的源碼編譯成若干目標模塊;展開來講編譯分爲三步編譯預處理(處理一些#定義的命令或語句)、編譯(語法語義分析)、彙編(彙編指令翻譯成機器指令)
- 鏈接:將若干目標模塊和所需的庫函數鏈接在一起,生成可執行程序
二者的區別:
- 編譯過程,檢查程序的語法以及函數、變量是否聲明,如果函數未聲明,編譯器會給出警告,但是會生成目標文件
- 鏈接過程,在目標文件中找尋函數的實現,如果未找到就會報鏈接錯誤碼
1 聲明 與 定義
1.1聲明
將一個名稱引入程序。
1.聲明一個函數:void fun(int a, int b);
2.聲明一個變量:extern int a;
3.聲明一個類:class A;
4.聲明一個類的靜態成員變量:class A{…;static int a;…}
5.聲明類型的別名:typedef int INT;
1.2定義
提供了一個實體在程序中的唯一描述。
1.定義一個類的靜態成員變量:int A::a = 0;
2.定義一個類的(非內聯)成員函數:int A::fun(){return a;}
1.3聲明和定義同時存在
大多數情況下聲明和定義是同時存在的
1.int a;
2.extern int a = 0;
1.4 聲明和定義的區別
在一個給定的作用域內,聲明可以有多次,定義只能有一次。但是類中的函數和靜態成員變量只能聲明一次
2 內部鏈接與外部鏈接
2.1編譯單元
當一個c或者cpp程序在編譯時,會遞歸其頭文件,形成一個含有必要信息的源文件,這個源文件就是一個編譯單元。
2.2內部鏈接
如果一個名稱對於它的編譯單元而言是局部的,不與其他編譯單元相同的名稱衝突,那麼這個名稱具有內部鏈接。
下面的名稱是內部鏈接:
1.所有的聲明
2.命名空間中的靜態自由函數(自由函數是指該函數不是類的成員函數,也不是友元函數)、靜態友元函數、靜態變量的定義
3.枚舉定義
4.內聯函數的定義
5.類的定義
6.命名空間中const常量定義
7.union定義
2.3 外部鏈接
在一個多文件程序中,如果一個名稱在鏈接時可以與其他編譯單元交互,那麼這個名稱就有外部鏈接。
外部鏈接的名稱:
1.類的非內聯函數,包括類的成員函數和靜態成員函數
2.類的靜態成員變量定義。(一定要和聲明區分開,聲明都是內部鏈接)
3.命名空間中的非靜態自由函數、非靜態友元函數、非靜態變量
2.4 總結
1.inline函數總有內部鏈接,不論這個函數是什麼函數,在類中的函數也是如此,所以inline函數最好放在頭文件中,每一個包含頭文件的函數都能夠找到inline函數
2.類定義總有內部鏈接,而非inline類成員函數總有外部鏈接,不論這個成員函數是靜態、虛擬還是一般成員函數,類的靜態數據成員定義總有外部鏈接
3.命名空間中的靜態自由函數,靜態友元函數,靜態變量,const常量定義都有內部鏈接
4.聲明、enum定義、union定義有內部鏈接
3 靜態鏈接 和 動態鏈接
二者的區別在於鏈接的時機不一樣,
- 靜態鏈接:在形成可執行程序之前
- 動態鏈接:在程序執行的過程中
3.1 靜態鏈接
將需要調用的函數或者過程鏈接到可執行文件中,成爲可執行程序的一部分,也就是說函數和過程的代碼就在exe文件中,該文件包含了運行中的全部代碼。
優缺點:
- 浪費空間,因爲每個可執行程序中都有一份其所需目標文件的拷貝,如果多個可執行程序對同一個目標文件有依賴,那該目標文件在內存中就有多個副本
- 更新困難,如果庫函數的代碼修改了,就需要重新編譯鏈接形成可執行程序
- 運行速度快,因爲可執行程序中已經具備了程序運行需要的所有東西
3.2 動態鏈接
動態鏈接就是爲了解決靜態鏈接存在浪費空間、更新困難的弊端而出現的。
動態鏈接是將程序按照模塊拆分成相對獨立的部分,在程序運行時,纔將他們鏈接在一起形成完整的程序
優缺點:
- 同一個目標文件在內存中只有一份
- 更新方便,只需將原來的目標文件替換即可,無需將所有的程序重新鏈接
- 運行速度會降低,因爲在運行可執行程序的過程中需要進行鏈接
3.4 如何選擇
如果系統中有多個應用程序使用這個庫,就把它編譯成動態庫,這樣在啓動的時候加載比較慢,但是多任務的時候會節省內存;
如果系統中只有一到兩個應用程序使用該庫,並且使用的API比較少,就編譯成靜態庫,這樣應用程序可能會比較大,但是啓動速度快。