實(shí)例詳解C/C++中extern關(guān)鍵字
1 基本解釋:extern可以置于變量或者函數(shù)前,以標(biāo)示變量或者函數(shù)的定義在別的文件中,提示編譯器遇到此變量和函數(shù)時(shí)在其他模塊中尋找其定義。此外extern也可用來進(jìn)行鏈接指定。
也就是說extern有兩個(gè)作用,第一個(gè),當(dāng)它與"C"一起連用時(shí),如: extern "C" void fun(int a, int b);則告訴編譯器在編譯fun這個(gè)函數(shù)名時(shí)按著C的規(guī)則去翻譯相應(yīng)的函數(shù)名而不是C++的,C++的規(guī)則在翻譯這個(gè)函數(shù)名時(shí)會(huì)把fun這個(gè)名字變得面目全非,可能是fun@aBc_int_int#%$也可能是別的,這要看編譯器的"脾氣"了(不同的編譯器采用的方法不一樣),為什么這么做呢,因?yàn)镃++支持函數(shù)的重載啊,在這里不去過多的論述這個(gè)問題,如果你有興趣可以去網(wǎng)上搜索,相信你可以得到滿意的解釋!
第二,當(dāng)extern不與"C"在一起修飾變量或函數(shù)時(shí),如在頭文件中: extern int g_Int; 它的作用就是聲明函數(shù)或全局變量的作用范圍的關(guān)鍵字,其聲明的函數(shù)和變量可以在本模塊活其他模塊中使用,記住它是一個(gè)聲明不是定義!也就是說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數(shù)時(shí),它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數(shù)或變量,但它不會(huì)報(bào)錯(cuò),它會(huì)在連接時(shí)從模塊A生成的目標(biāo)代碼中找到此函數(shù)。
2 問題:extern 變量
在一個(gè)源文件里定義了一個(gè)數(shù)組:char a[6];
在另外一個(gè)文件里用下列語句進(jìn)行了聲明:extern char *a;
請(qǐng)問,這樣可以嗎?
答案與分析:
1)、不可以,程序運(yùn)行時(shí)會(huì)告訴你非法訪問。原因在于,指向類型T的指針并不等價(jià)于類型T的數(shù)組。extern char *a聲明的是一個(gè)指針變量而不是字符數(shù)組,因此與實(shí)際的定義不同,從而造成運(yùn)行時(shí)非法訪問。應(yīng)該將聲明改為extern char a[ ]。
2)、例子分析如下,如果a[] = "abcd",則外部變量a=0x61626364 (abcd的ASCII碼值),*a顯然沒有意義
顯然a指向的空間(0x61626364)沒有意義,易出現(xiàn)非法內(nèi)存訪問。
3)、這提示我們,在使用extern時(shí)候要嚴(yán)格對(duì)應(yīng)聲明時(shí)的格式,在實(shí)際編程中,這樣的錯(cuò)誤屢見不鮮。
4)、extern用在變量聲明中常常有這樣一個(gè)作用,你在*.c文件中聲明了一個(gè)全局的變量,這個(gè)全局的變量如果要被引用,就放在*.h中并用extern來聲明。
3 問題:當(dāng)方面修改extern 函數(shù)原型
當(dāng)函數(shù)提供方單方面修改函數(shù)原型時(shí),如果使用方不知情繼續(xù)沿用原來的extern申明,這樣編譯時(shí)編譯器不會(huì)報(bào)錯(cuò)。但是在運(yùn)行過程中,因?yàn)樯倭嘶蛘叨嗔溯斎雲(yún)?shù),往往會(huì)照成系統(tǒng)錯(cuò)誤,這種情況應(yīng)該如何解決?
答案與分析:
目前業(yè)界針對(duì)這種情況的處理沒有一個(gè)很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供對(duì)外部接口的聲明,然后調(diào)用方include該頭文件,從而省去extern這一步。以避免這種錯(cuò)誤。
寶劍有雙鋒,對(duì)extern的應(yīng)用,不同的場(chǎng)合應(yīng)該選擇不同的做法。
4 問題:extern “C”
在C++環(huán)境下使用C函數(shù)的時(shí)候,常常會(huì)出現(xiàn)編譯器無法找到obj模塊中的C函數(shù)定義,從而導(dǎo)致鏈接失敗的情況,應(yīng)該如何
解決這種情況呢?
答案與分析:
C++語言在編譯的時(shí)候?yàn)榱私鉀Q函數(shù)的多態(tài)問題,會(huì)將函數(shù)名和參數(shù)聯(lián)合起來生成一個(gè)中間的函數(shù)名稱,而C語言則不會(huì),因此會(huì)造成鏈接時(shí)找不到對(duì)應(yīng)函數(shù)的情況,此時(shí)C函數(shù)就需要用extern “C”進(jìn)行鏈接指定,這告訴編譯器,請(qǐng)保持我的名稱,不要給我生成用于鏈接的中間函數(shù)名。
下面是一個(gè)標(biāo)準(zhǔn)的寫法:
//在.h文件的頭上 #ifdef __cplusplus #if __cplusplus extern "C"{ #endif #endif /* __cplusplus */ … … //.h文件結(jié)束的地方 #ifdef __cplusplus #if __cplusplus } #endif #endif /* __cplusplus */
5 問題:extern 函數(shù)聲明
常常見extern放在函數(shù)的前面成為函數(shù)聲明的一部分,那么,C語言的關(guān)鍵字extern在函數(shù)的聲明中起什么作用?
答案與分析:
如果函數(shù)的聲明中帶有關(guān)鍵字extern,僅僅是暗示這個(gè)函數(shù)可能在別的源文件里定義,沒有其它作用。即下述兩個(gè)函數(shù)聲明沒有明顯的區(qū)別:
extern int f(); 和int f();
當(dāng)然,這樣的用處還是有的,就是在程序中取代include “*.h”來聲明函數(shù),在一些復(fù)雜的項(xiàng)目中,我比較習(xí)慣在所有的函數(shù)聲明前添加extern修飾。關(guān)于這樣做的原因和利弊可見下面的這個(gè)例子:“用extern修飾的全局變量”
(1) 在test1.h中有下列聲明:
#ifndef TEST1H #define TEST1H extern char g_str[]; // 聲明全局變量g_str void fun1(); #endif
(2) 在test1.cpp中
#include "test1.h" char g_str[] = "123456"; // 定義全局變量g_str void fun1() { cout << g_str << endl; }
(3) 以上是test1模塊, 它的編譯和連接都可以通過,如果我們還有test2模塊也想使用g_str,只需要在原文件中引用就可以了
#include "test1.h" void fun2() { cout << g_str << endl; }
以上test1和test2可以同時(shí)編譯連接通過,如果你感興趣的話可以用ultraEdit打開test1.obj,你可以在里面找到"123456"這個(gè)字符串,但是你卻不能在test2.obj里面找到,這是因?yàn)間_str是整個(gè)工程的全局變量,在內(nèi)存中只存在一份,test2.obj這個(gè)編譯單元不需要再有一份了,不然會(huì)在連接時(shí)報(bào)告重復(fù)定義這個(gè)錯(cuò)誤!
(4) 有些人喜歡把全局變量的聲明和定義放在一起,這樣可以防止忘記了定義,如把上面test1.h改為
extern char g_str[] = "123456"; // 這個(gè)時(shí)候相當(dāng)于沒有extern
然后把test1.cpp中的g_str的定義去掉,這個(gè)時(shí)候再編譯連接test1和test2兩個(gè)模塊時(shí),會(huì)報(bào)連接錯(cuò)誤,這是因?yàn)槟惆讶肿兞縢_str的定義放在了頭文件之后,test1.cpp這個(gè)模塊包含了test1.h所以定義了一次g_str,而test2.cpp也包含了test1.h所以再一次定義了g_str,這個(gè)時(shí)候連接器在連接test1和test2時(shí)發(fā)現(xiàn)兩個(gè)g_str。如果你非要把g_str的定義放在test1.h中的話,那么就把
test2的代碼中#include "test1.h"去掉 換成:
extern char g_str[]; void fun2() { cout << g_str << endl; }
這個(gè)時(shí)候編譯器就知道g_str是引自于外部的一個(gè)編譯模塊了,不會(huì)在本模塊中再重復(fù)定義一個(gè)出來,但是我想說這樣做非常糟糕,因?yàn)槟阌捎跓o法在test2.cpp中使用#include "test1.h",那么test1.h中聲明的其他函數(shù)你也無法使用了,除非也用都用extern修飾,這樣的話你光聲明的函數(shù)就要一大串,而且頭文件的作用就是要給外部提供接口使用的,所以 請(qǐng)記住, 只在頭文件中做聲明,真理總是這么簡(jiǎn)單。
6. extern 和 static
(1) extern 表明該變量在別的地方已經(jīng)定義過了,在這里要使用那個(gè)變量.
(2) static 表示靜態(tài)的變量,分配內(nèi)存的時(shí)候, 存儲(chǔ)在靜態(tài)區(qū),不存儲(chǔ)在棧上面.
static 作用范圍是內(nèi)部連接的關(guān)系, 和extern有點(diǎn)相反.它和對(duì)象本身是分開存儲(chǔ)的,extern也是分開存儲(chǔ)的,但是extern可以被其他的對(duì)象用extern 引用,而static 不可以,只允許對(duì)象本身用它. 具體差別首先,static與extern是一對(duì)“水火不容”的家伙,也就是說extern和static不能同時(shí)修飾一個(gè)變量;其次,static修飾的全局變量聲明與定義同時(shí)進(jìn)行,也就是說當(dāng)你在頭文件中使用static聲明了全局變量后,它也同時(shí)被定義了;最后,static修飾全局變量的作用域只能是本身的編譯單元,也就是說它的“全局”只
對(duì)本編譯單元有效,其他編譯單元?jiǎng)t看不到它,如:
(1) test1.h:
#ifndef TEST1H #define TEST1H static char g_str[] = "123456"; void fun1(); #endif
(2) test1.cpp:
#include "test1.h" void fun1() { cout << g_str << endl; }
(3) test2.cpp
#include "test1.h" void fun2() { cout << g_str << endl; }
以上兩個(gè)編譯單元可以連接成功, 當(dāng)你打開test1.obj時(shí),你可以在它里面找到字符串"123456",同時(shí)你也可以在test2.obj中找到它們,它們之所以可以連接成功而沒有報(bào)重復(fù)定義的錯(cuò)誤是因?yàn)殡m然它們有相同的內(nèi)容,但是存儲(chǔ)的物理地址并不一樣,就像是兩個(gè)不同變量賦了相同的值一樣,而這兩個(gè)變量分別作用于它們各自的編譯單元。 也許你比較較真,自己偷偷的跟蹤調(diào)試上面的代碼,結(jié)果你發(fā)現(xiàn)兩個(gè)編譯單元(test1,test2)的g_str的內(nèi)存地址相同,于是你下結(jié)論static修飾的變量也可以作用于其他模塊,但是我要告訴你,那是你的編譯器在欺騙你,大多數(shù)編譯器都對(duì)代碼都有優(yōu)化功能,以達(dá)到生成的目標(biāo)程序更節(jié)省內(nèi)存,執(zhí)行效率更高,當(dāng)編譯器在連接各個(gè)編譯單元的時(shí)候,它會(huì)把相同內(nèi)容的內(nèi)存只拷貝一份,比如上面的"123456", 位于兩個(gè)編譯單元中的變量都是同樣的內(nèi)容,那么在連接的時(shí)候它在內(nèi)存中就只會(huì)存在一份了,如果你把上面的代碼改成下面的樣子,你馬上就可以拆穿編譯器的謊言:
(1) test1.cpp:
#include "test1.h" void fun1() { g_str[0] = ''a''; cout << g_str << endl; }
(2) test2.cpp
#include "test1.h" void fun2() { cout << g_str << endl; }
(3) void main() {
fun1(); // a23456 fun2(); // 123456 }
這個(gè)時(shí)候你在跟蹤代碼時(shí),就會(huì)發(fā)現(xiàn)兩個(gè)編譯單元中的g_str地址并不相同,因?yàn)槟阍谝惶幮薷牧怂跃幾g器被強(qiáng)行的恢復(fù)內(nèi)存的原貌,在內(nèi)存中存在了兩份拷貝給兩個(gè)模塊中的變量使用。正是因?yàn)閟tatic有以上的特性,所以一般定義static全局變量時(shí),都把它放在原文件中而不是頭文件,這樣就不會(huì)給其他模塊造成不必要的信息污染,同樣記住這個(gè)原則吧!
7. extern 和const
C++中const修飾的全局常量據(jù)有跟static相同的特性,即它們只能作用于本編譯模塊中,但是const可以與extern連用來聲明該常量可以作用于其他編譯模塊中, 如extern const char g_str[];
然后在原文件中別忘了定義: const char g_str[] = "123456";
所以當(dāng)const單獨(dú)使用時(shí)它就與static相同,而當(dāng)與extern一起合作的時(shí)候,它的特性就跟extern的一樣了!所以對(duì)const我沒有什么可以過多的描述,我只是想提醒你,const char* g_str = "123456" 與 const char g_str[] ="123465"是不同的, 前面那個(gè)const 修飾的是char *而不是g_str,它的g_str并不是常量,它被看做是一個(gè)定義了的全局變量(可以被其他編譯單元使用), 所以如果你像讓char*g_str遵守const的全局常量的規(guī)則,最好這么定義const char* const g_str="123456".
欄 目:C語言
下一篇:舉例講解C語言程序中對(duì)二叉樹數(shù)據(jù)結(jié)構(gòu)的各種遍歷方式
本文標(biāo)題:實(shí)例詳解C/C++中extern關(guān)鍵字
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2396.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10求子數(shù)組最大和的解決方法詳解
- 01-10深入二叉樹兩個(gè)結(jié)點(diǎn)的最低共同父結(jié)點(diǎn)的詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)- 解析最少換車次數(shù)的問題詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)-用棧實(shí)現(xiàn)表達(dá)式求值的方法詳解
- 01-10HDOJ 1443 約瑟夫環(huán)的最新應(yīng)用分析詳解
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10如何查看進(jìn)程實(shí)際的內(nèi)存占用情況詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對(duì)數(shù)函數(shù)的表達(dá)式 c語言中對(duì)
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 01-11ajax實(shí)現(xiàn)頁面的局部加載