[C++] 變量和函數的鏈接性

1. 單獨編譯

和C語言一樣,C++也允許甚至鼓勵程序員將組件函數放在獨立的文件中。
可以單獨編譯這些文件,然后將它們鏈接成可執行的程序。
通常C++編譯器既編譯程序,也管理鏈接器。
如果只修改了一個文件,則可以只重新編譯該文件,然后將它與其他文件的編譯版本鏈接。
這使得大程序的管理更便捷。

頭文件中常包含以下內容:
函數原型,使用#defineconst定義的符號常量,結構聲明,類聲明,模板聲明,內聯函數。

注意在包含頭文件時,我們使用coordin.h而不是<coordin.h>
如果文件名包含在尖括號中,則C++編譯器將在存儲標準頭文件的主機系統的文件系統中查找,
但如果文件名包含在雙引號中,則C++編譯器首先查找當前工作目錄或源代碼目錄(或其他目錄,這取決于編譯器),
如果沒有在那里找到頭文件,則將在標準位置查找。
因此,在包含自己的頭文件時,應使用引號而不是尖括號。

2. 存儲持續性

C++使用三種(在C++11中是四種)不同的方案來存儲數據,這些方案的區別就在于數據保留在內存中的時間。

(1)自動存儲持續性
在函數定義中聲明的變量(包括函數參數)的存儲持續性為自動的,
它們在程序開始執行其所屬的函數或代碼塊時被創建,在執行完函數或代碼塊時,它們使用的內存被釋放。

(2)靜態存儲持續性
在函數外定義的變量,和使用關鍵字static定義的變量的存儲持續性都為靜態。
它們在程序整個運行過程中都存在。

(3)線程存儲持續性(C++11)
當前多核處理器很常見,這些CPU可同時處理多個執行任務。
這讓程序能夠將計算放在可并行處理的不同線程中。
如果變量是使用關鍵字thread_local聲明的,則其生命周期與所屬的線程一樣長。

(4)動態存儲持續性
new運算符分配的內存將一直存在,直到使用delete運算符將其釋放或程序結束為止。
這種內存的存儲持續性為動態,有時被稱為自由存儲(free store)或堆(heap)。

注:
在C++11中,關鍵字auto用于自動類型推斷,
但在C語言和以前的C++版本中,auto的含義截然不同,它用于顯式的指出變量為自動存儲。

3. 作用域

作用域(scope)描述了名稱在文件(編譯單元)的多大范圍內可見。
例如,函數中定義的變量可在該函數中使用,但不能在其他函數中使用。
而在文件中的函數定義之前定義的變量則可在所有函數中使用。

C++變量的作用域有多種,作用域為局部的變量只在定義它的代碼塊中可用。
代碼塊是由花括號括起的一系列語句。
作用域為全局(也叫文件作用域)的變量,在定義位置到文件結尾之間都可用。

自動變量的作用域為局部,
靜態變量的作用域是全局還是局部取決于它是如何被定義的。
在函數原型作用域中使用的名稱只能在包含參數列表的括號內可用(這就是為什么這些名稱是什么以及是否出現都不重要的原因)。
在類中聲明的成員的作用域為整個類,
在名稱空間中聲明的變量的作用域為整個名稱空間(由于名稱空間已經引入到C++語言中,因此全局作用域是名稱空間作用域的特例)。

4. 變量的鏈接性

鏈接性(linkage)描述了名稱如何在不同單元間共享。
鏈接性為外部的名稱,可在文件間共享,鏈接性為內部的名稱,只能由一個文件中的函數共享。
自動變量的名稱沒有鏈接性,因為它不能共享。

鏈接性為外部的變量,通常簡稱為外部變量,它們的存儲持續性為靜態,作用域為整個文件。

4.1 extern

一方面,在每個使用外部變量的文件中,都必須聲明它,
另一方面,變量只能有一次定義。
C++提供了兩種操作,一種稱為定義,它給變量分配存儲空間。
另一種是引用聲明,它不給變量分配存儲空間,而是引用已有的變量。

引用聲明使用關鍵字extern,且不進行初始化,否則將變成定義,導致分配空間。

double up;    // definition, up is 0
extern int blem;    // blem defined elsewhere
extern char gr = 'z';    // definition because initialized

extern char gr = 'z';中的extern是可以省略的。

如果在多個文件中使用外部變量,只需要在一個文件中包含該變量的定義,
但在使用該變量的其他所有文件中,都必須使用關鍵字extern聲明它。

注:
省略extern將使變量具有外部鏈接屬性。

4.2 static

(1)用于作用域為整個文件的變量時

static限定符用于作用域為整個文件的變量時,該變量的鏈接性將為內部的。
鏈接性為內部的變量只能在其所屬的文件中使用。

如果文件定義了一個靜態變量,其名稱與另一個文件中聲明的常規外部變量相同,
則在該文件中,靜態變量將隱藏常規外部變量。

// file1
int errors = 20;    // external declaration

- - -
// file2
static int errors = 5;    // known to file2 only

void froobish(){
    cout << errors;    // uses errors defined in file2
}

可使用外部變量在多文件程序的不同部分之間共享數據,
可使用鏈接性為內部的靜態變量在同一個文件中的多個函數之間共享數據。
(名稱空間提供了另外一種共享數據的方法)
如果將作用域為整個文件的變量變為靜態的,就不必擔心其名稱與其他文件中的作用域為整個文件的變量發生沖突。

(2)用于作用域為整個文件的變量時
static限定符用于在代碼塊中定義的變量時,將導致局部變量的存儲持續性為靜態的。
這意味著雖然該變量只在該代碼塊中可用,但它在該代碼塊不處于活動狀態時仍然存在。
因此,在兩次函數調用之間,靜態局部變量的值將保持不變。

另外,如果初始化了靜態局部變量,則程序只在啟動時進行一次初始化。
以后再調用函數時,將不會像自動變量那樣再次被初始化。

注:
關鍵字static被用在作用域為整個文件的聲明中時,表示內部鏈接性,
被用于局部聲明中,表示局部變量的存儲持續性為靜態的。

關鍵字extern表示引用聲明,即聲明引用在其他地方定義的變量。
關鍵字thread_local指出變量的持續性與其所屬線程的持續性相同。
thread_local變量之于線程,猶如常規靜態變量之于整個程序。

5. 函數的鏈接性

在默認情況下,函數的鏈接性為外部的,即可以在文件間共享。
可以在函數原型中使用關鍵字extern來指出函數是在另一個文件中定義的,不過這是可選的。

可以使用關鍵字static將函數的鏈接性設置為內部的,使之只能在一個文件中使用。
必須同時在原型和函數定義中使用static關鍵字。

static int fn(double x);

...
static int fn(double x){
    ...
}

這意味著該函數只在這個文件中可見,還意味著可以在其他文件中定義同名的函數。
和變量一樣,在定義靜態函數的文件中,靜態函數將覆蓋外部定義,
因此,即使在外部定義了同名的函數,該文件扔將使用靜態函數。

單定義規則也適用于非內聯函數,因此對于每個非內聯函數,程序只能包含一個定義。
對于鏈接性味外部的函數來說,這意味著在多文件程序中,只能有一個文件包含該函數的定義,
但使用該函數的每個文件都應包含其函數原型。

內聯函數不受這種規則的約束,這允許程序員能夠將內聯函數的定義放在頭文件中,
這樣包含了頭文件的每個文件都有內聯函數的定義。
然而,C++要求同一個函數的素有內聯定義都必須相同。

注:
在默認情況下,函數的鏈接性為外部的,即可以在文件間共享。

6. 小結

所有聲明,都具有外部連接性。

6.1 具有內部連接性的定義

(1)名字空間(包括全局名字空間)中的靜態自由函數,靜態友元函數、靜態變量定義,const常量定義
(2)enum定義,類的定義,union的定義
(3)inline函數定義(包括自由函數和非自由函數)

6.2 具有外部連接性的定義

(1)非inline的類成員函數,非inline的類靜態函數
(2)類靜態成員變量
(3)名字空間(包括全局名字空間)中非靜態自由函數,非靜態友元函數,非靜態變量

注:
把一個帶有外部連接的定義放在 .h 文件中都會引起錯誤,
由于類的聲明和定義都是內部連接的,一般都放在 .h 文件中。


參考

C++ Primer Plus, 6th - P300~P319
C++ 概念兩則:聲明和定義,內部連接和外部連接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容