淺談C/C++中的static和extern關(guān)鍵字

1 原理

1.1 首先,關(guān)于聲明和定義的區(qū)別。

這種寫法(函數(shù)原型后加;號表示結(jié)束的寫法)只能叫函數(shù)聲明而不能叫函數(shù)定義,只有帶函數(shù)體的聲明才叫定義,比如下面

只有分配存儲空間的變量聲明才叫變量定義,其實(shí)函數(shù)也是一樣,編譯器只有見到函數(shù)定義才會生成指令,而指令在程序運(yùn)行時當(dāng)然也要占存儲空間。那么沒有函數(shù)體的函數(shù)聲明有什么用呢?它為編譯器提供了有用的信息,編譯器在翻譯代碼的過程中,只有見到函數(shù)原型(不管帶不帶函數(shù)體)之后才知道這個函數(shù)的名字、參數(shù)類型和返回值,這樣碰到函數(shù)調(diào)用時才知道怎么生成相應(yīng)的指令,所以函數(shù)原型必須出現(xiàn)在函數(shù)調(diào)用之前,這也是遵循“先聲明后使用”的原則。

1.2 標(biāo)識符的鏈接屬性(Linkage)有三種

外部鏈接(ExternalLinkage)

如果最終的可執(zhí)行文件由多個程序文件鏈接而成,一個標(biāo)識符在任意程序文件中即使聲明多次也都代表同一個變量或函數(shù),則這個標(biāo)識符具有External Linkage。具有External Linkage的標(biāo)識符編譯后在符號表中是GLOBAL的符號。

內(nèi)部鏈接(InternalLinkage)

如果一個標(biāo)識符在某個程序文件中即使聲明多次也都代表同一個變量或函數(shù),則這個標(biāo)識符具有Internal Linkage。具有Internal Linkage的標(biāo)識符編譯后在符號表中是LOCAL的符號。

無鏈接(NoLinkage)

除以上情況之外的標(biāo)識符都屬于No Linkage的,例如函數(shù)的局部變量,以及不表示變量和函數(shù)的其它標(biāo)識符。

1.3 聲明和定義的次數(shù)限制

凡是被多次聲明的變量或函數(shù),必須有且只有一個聲明是定義,如果有多個定義,或者一個定義都沒有,鏈接器就無法完成鏈接。顯然,聲明可以有很多次。

2 用extern和static修飾函數(shù)

測試案例2.1

測試案例2.1結(jié)果

編譯不能通過,error C2129: 靜態(tài)函數(shù)“void print_03(void)”已聲明但未定義因?yàn)閜rint_03在extern_static_test1.h中被聲明為static。當(dāng)static修飾函數(shù)的時候,說明此函數(shù)只能被自己內(nèi)部的文件使用,即具有internal linkage.因此main不能調(diào)用extern_static_test1.h中被static修飾的函數(shù)。如有有這樣幾個文件a.h, a.cpp, main.cpp。其中a.h被a.cpp和main.cpp所包含,那么a.h中用static修飾的函數(shù)只能被a.cpp中的函數(shù)調(diào)用,不能被main.cpp中的函數(shù)調(diào)用.這個internal linkage屬性就被確定了。即使你故意在main.cpp中進(jìn)行一次external的聲明(如extern void print_03(void);)也不能改變此internal linkage鏈接屬性。

測試案例2.2

為了修正測試案例2.1的錯誤,在main.cpp中刪除print_03();其它文件保持不變

則編譯沒有錯誤了,運(yùn)行結(jié)果是,

測試案例2.2結(jié)果

1. ?print_02()說明,函數(shù)被聲明為external后,才能做到在多個文件中多次聲明的情況下,依然指示同一個定義。

2. ?print_01()說明,函數(shù)聲明如果不加external,也不加static,則默認(rèn)為external.

3.

print_04()說明,如果函數(shù)聲明使用了static修飾符,則這個函數(shù)具有internallinkage,只能被聲明所在的文件內(nèi)部調(diào)用。所以這里的print_04()調(diào)用了聲明為static的print_03()。print_04()不具有static屬性,所以在main.cpp中能夠被調(diào)用。

測試案例2.3

在main.cpp中強(qiáng)制用extern修飾符聲明print_03();其它文件保持不變會發(fā)生什么?

測試案例2.3結(jié)果

依然出現(xiàn)編譯錯誤。說明,在a.h中已經(jīng)被聲明為static的文件,被main.cpp包含之后,main.cpp中不能修改它的屬性為external。

3 用extern和static修飾變量

測試案例3.1

由于是變量定義,所以不寫在extern_static_test1.h中,extern_static_test1.h和上文保持基本一致,為了簡化,只保留了一個函數(shù)。

變量定義寫在了extern_static_test1.cpp中,如下

現(xiàn)在來看看main.cpp文件

測試案例3.1結(jié)果

編譯發(fā)現(xiàn),var1和var3都出現(xiàn)了編譯錯誤。

這說明,雖然已經(jīng)在這里喪心病狂地對3個變量的生命都寫明了extern.

extern int var1;

extern int var2;

extern int var3

但是,因?yàn)樵趀xtern_static_test1.cpp的文件中,var3已經(jīng)被定義為了static,所以它具有internallinkage了,只能在extern_static_test1.cpp中被使用了,不能在main.cpp中被使用了;因?yàn)樵趀xtern_static_test1.cpp的文件中,var1沒有修飾符,變量如果沒有鏈接屬性的修飾符,默認(rèn)是static。這和函數(shù)正好相反,函數(shù)如果沒有鏈接屬性的修飾符,默認(rèn)是external。想想,這樣設(shè)定是符合實(shí)際需求的,函數(shù)就是用來進(jìn)行操作,被調(diào)用的,默認(rèn)external更方便,變量不應(yīng)該被隨意使用,而應(yīng)該被函數(shù)操作,這樣才安全。

main.cpp修改之后,正確

測試案例3.2

如果把main.cpp中external聲明的關(guān)鍵字external去掉,extern_static_test1.h和extern_static_test1.cpp保持不變,這樣是否正確呢?

測試案例3.2結(jié)果

出現(xiàn)編譯錯誤,說明,如果要使得一個變量具有external linkage,必須在定義時和聲明時都得加上external修飾符,比如這里需要在extern_static_test1.cpp中var2的定義和main.cpp中的var2的聲明中都寫上external修飾符。因?yàn)樽兞磕J(rèn)是static,你要不特別說明它是external,那就默認(rèn)是static了。



二者的一些定性說明:

static:

一、在C中,static主要定義全局靜態(tài)變量、定義局部靜態(tài)變量、定義靜態(tài)函數(shù)。

1、定義全局靜態(tài)變量:在全局變量前面加上關(guān)鍵字static,該全局變量變成了全局靜態(tài)變量。全局靜態(tài)變量有以下特點(diǎn)。

???????????? a.在全局區(qū)分配內(nèi)存。

?????????? ? b.如果沒有初始化,其默認(rèn)值為0.

????????? ? c.該變量在本文件內(nèi)從定義開始到文件結(jié)束可見。

2、定義局部靜態(tài)變量:在局部變量前面加上關(guān)鍵字static,其特點(diǎn)如下:

???????????? a.該變量在全局?jǐn)?shù)據(jù)區(qū)分配內(nèi)存。

??????????? b.它始終駐留在全局?jǐn)?shù)據(jù)區(qū),直到程序運(yùn)行結(jié)束。

??????????? c. 其作用域?yàn)榫植孔饔糜颍?dāng)定義它的函數(shù)或語句塊結(jié)束時,其作用域隨之結(jié)束。

3、定義靜態(tài)函數(shù):在函數(shù)返回類型前加上static關(guān)鍵字,函數(shù)即被定義為靜態(tài)函數(shù),其特點(diǎn)如下:

???????????? a.靜態(tài)函數(shù)只能在本源文件中使用

????????????? b.在文件作用域中聲明的inline函數(shù)默認(rèn)為static類型

二、在C++中新增了兩種作用:定義靜態(tài)數(shù)據(jù)成員或靜態(tài)函數(shù)成員。

定義靜態(tài)數(shù)據(jù)成員。

????? ? ? ? ? a.內(nèi)存分配:靜態(tài)數(shù)據(jù)成員在程序的全局?jǐn)?shù)據(jù)區(qū)分配。

????? ? ? ?? ? b.初始化和定義:靜態(tài)數(shù)據(jù)成員定義時要分配空間,所以不能在類聲明中定義。

????????? 靜態(tài)數(shù)據(jù)成員因?yàn)槌绦蛞蚤_始運(yùn)行就必須存在,所以其初始化的最佳位置在類的內(nèi)部,public、protected、private關(guān)鍵字對它的限定和普通數(shù)據(jù)成員一樣,因?yàn)槠淇臻g在全局?jǐn)?shù)據(jù)分配,屬于所有本類的對象共享。它不屬于特定的類對象,在沒產(chǎn)生類對象時,其作用域可見,即沒有產(chǎn)生類的實(shí)例時,就可以操作它了。

靜態(tài)成員函數(shù)。靜態(tài)成員函數(shù)與類相聯(lián)系,不與類的對象相聯(lián)系。靜態(tài)成員函數(shù)不能訪問非靜態(tài)數(shù)據(jù)成員。

extern:

extern可以置于變量或函數(shù)前,以在別的文件中標(biāo)識變量或函數(shù)的定義,并提示編譯器遇到此變量或函數(shù)時在其他模塊中尋找其定義。extern是C、C++語言中表明函數(shù)和全局變量作用范圍(可見性)的關(guān)鍵字。對于extern變量來說,僅僅是一個變量的聲明,其并不是定義,不會分配內(nèi)存空間。extern表示將變量或函數(shù)聲明為外部鏈接,變量默認(rèn)是內(nèi)部鏈接,函數(shù)默認(rèn)是外部鏈接。因此用來外部鏈接的函數(shù),聲明時有無extern都可以連接通過。而全局變量則不行。通常,在模塊的頭文件中,對本模塊提供給其他模塊引用的函數(shù)和全局變量以關(guān)鍵字extern聲明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • C++關(guān)鍵字的思考 本章內(nèi)容:1 關(guān)鍵字的相關(guān)理解1.1 const關(guān)鍵字1.2 static關(guān)鍵字1.3 非局部...
    Haley_2013閱讀 785評論 0 50
  • C++的static有兩種用法:面向過程程序設(shè)計(jì)中的static和面向?qū)ο蟪绦蛟O(shè)計(jì)中的static。前者應(yīng)用于普通...
    yangqi916閱讀 397評論 0 0
  • 總有那么一個時刻,你的耳機(jī)中會單曲循環(huán)著一首歌,說不出為什么,就是覺得那個時間,那首歌剛剛好。 許久不曾寫文章,手...
    周詩汶閱讀 366評論 0 0
  • 我慶幸我厚顏無恥,不斷的擺弄我那不堪入目的文字,展示我自以為的學(xué)富五車。我慶幸的是,我能把我的情緒寫出來,讓別人知...
    沐府墓主閱讀 198評論 0 1
  • 畢業(yè),這兩個字意味著責(zé)任與擔(dān)當(dāng)。此刻面臨畢業(yè)的我們,想得不再是今天逃課還好沒被老師發(fā)現(xiàn);期末考試一定要左右逢源,天...
    38f9fa77010c閱讀 275評論 0 1