1. 程式人生 > >C++中extern關鍵字的作用

C++中extern關鍵字的作用

基本解釋:extern可以置於變數或者函式前,以標示變數或者函式的定義在別的檔案中提示編譯器遇到此變數和函式時在其他模組中尋找其定義。此外extern也可用來進行連結指定。

      也就是說extern有兩個作用,第一個,當它與"C"一起連用時,如: extern "C" void fun(int a, int b);則告訴編譯器在編譯fun這個函式名時按著C的規則去翻譯相應的函式名而不是C++的,C++的規則在翻譯這個函式名時會把fun這個名字變得面目全非,可能是[email protected]_int_int#%$也可能是別的,這要看編譯器的"脾氣"了(不同的編譯器採用的方法不一樣),為什麼這麼做呢,因為C++支援函式的過載啊,在這裡不去過多的論述這個問題,如果你有興趣可以去網上搜索,相信你可以得到滿意的解釋!
    第二,當extern不與"C"在一起修飾變數或函式時,如在標頭檔案中: extern int g_Int; 它的作用就是宣告函式或全域性變數的作用範圍的關鍵字,其宣告的函式和變數可以在本模組活其他模組中使用,記住它是一個宣告不是定義!也就是說B模組(編譯單元)要是引用模組(編譯單元)A中定義的全域性變數或函式時,它只要包含A模組的標頭檔案即可,在編譯階段,模組B雖然找不到該函式或變數,但它不會報錯,它會在連線時從模組A生成的目的碼中找到此函式。

2 問題:extern 變數
  在一個原始檔裡定義了一個數組:char a[6];
  在另外一個檔案裡用下列語句進行了宣告:extern char *a;
  請問,這樣可以嗎? 
  答案與分析:
  1)、不可以,程式執行時會告訴你非法訪問。原因在於,指向型別T的指標並不等價於型別T的陣列。extern char *a宣告的是一個指標變數而不是字元陣列,因此與實際的定義不同,從而造成執行時非法訪問。應該將宣告改為extern char a[ ]
  2)、例子分析如下,如果a[] = "abcd",則外部變數a=0x61626364 (abcd的ASCII碼值),*a顯然沒有意義
  顯然a指向的空間(0x61626364)沒有意義,易出現非法記憶體訪問。
  3)、這提示我們,在使用extern時候要嚴格對應宣告時的格式,在實際程式設計中,這樣的錯誤屢見不鮮。
  4)、extern用在變數宣告中常常有這樣一個作用,你在*.c檔案中聲明瞭一個全域性的變數,這個全域性的變數如果要被引用,就放在*.h中並用extern來宣告

3 問題:當方面修改extern 函式原型
  當函式提供方單方面修改函式原型時,如果使用方不知情繼續沿用原來的extern申明,這樣編譯時編譯器不會報錯。但是在執行過程中,因為少了或者多了輸入引數,往往會照成系統錯誤,這種情況應該如何解決?
  答案與分析:
  目前業界針對這種情況的處理沒有一個很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供對外部介面的宣告,然後呼叫方include該標頭檔案,從而省去extern這一步。以避免這種錯誤。
  寶劍有雙鋒,對extern的應用,不同的場合應該選擇不同的做法。

4 問題:extern “C”
  在C++環境下使用C函式的時候,常常會出現編譯器無法找到obj模組中的C函式定義,從而導致連結失敗的情況,應該如何解決這種情況呢?

  答案與分析:
  C++語言在編譯的時候為了解決函式的多型問題,會將函式名和引數聯合起來生成一箇中間的函式名稱,而C語言則不會,因此會造成連結時找不到對應函式的情況,此時C函式就需要用extern “C”進行連結指定,這告訴編譯器,請保持我的名稱,不要給我生成用於連結的中間函式名。
  下面是一個標準的寫法:
//在.h檔案的頭上
#ifdef __cplusplus
#if __cplusplus
extern "C"{
 #endif
 #endif /* __cplusplus */ 
 …
 …
 //.h檔案結束的地方
 #ifdef __cplusplus
 #if __cplusplus
}
#endif
#endif /* __cplusplus */ 

5 問題:extern 函式宣告
  常常見extern放在函式的前面成為函式宣告的一部分,那麼,C語言的關鍵字extern在函式的宣告中起什麼作用?
  答案與分析:
  如果函式的宣告中帶有關鍵字extern,僅僅是暗示這個函式可能在別的原始檔裡定義,沒有其它作用。即下述兩個函式宣告沒有明顯的區別:
extern int f(); 和int f();
  當然,這樣的用處還是有的,就是在程式中取代include “*.h”來宣告函式,在一些複雜的專案中,我比較習慣在所有的函式宣告前新增extern修飾。關於這樣做的原因和利弊可見下面的這個例子:“用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可以同時編譯連線通過,如果你感興趣的話可以用ultraEdit開啟test1.obj,你可以在裡面找到"123456"這個字串,但是你卻不能在test2.obj裡面找到,這是因為g_str是整個工程的全域性變數,在記憶體中只存在一份,test2.obj這個編譯單元不需要再有一份了,不然會在連線時報告重複定義這個錯誤!
    (4) 有些人喜歡把全域性變數的宣告和定義放在一起,這樣可以防止忘記了定義,如把上面test1.h改為
    extern char g_str[] = "123456"; // 這個時候相當於沒有extern
    然後把test1.cpp中的g_str的定義去掉,這個時候再編譯連線test1和test2兩個模組時,會報連線錯誤,這是因為你把全域性變數g_str的定義放在了標頭檔案之後,test1.cpp這個模組包含了test1.h所以定義了一次g_str,而test2.cpp也包含了test1.h所以再一次定義了g_str,這個時候聯結器在連線test1和test2時發現兩個g_str。如果你非要把g_str的定義放在test1.h中的話,那麼就把test2的程式碼中#include "test1.h"去掉 換成:
    extern char g_str[];
    void fun2()   {  cout << g_str << endl;   }
   這個時候編譯器就知道g_str是引自於外部的一個編譯模組了,不會在本模組中再重複定義一個出來,但是我想說這樣做非常糟糕,因為你由於無法在test2.cpp中使用#include "test1.h",那麼test1.h中宣告的其他函式你也無法使用了,除非也用都用extern修飾,這樣的話你光宣告的函式就要一大串,而且標頭檔案的作用就是要給外部提供介面使用的,所以 請記住, 只在標頭檔案中做宣告,真理總是這麼簡單

6. extern 和 static

 (1) extern 表明該變數在別的地方已經定義過了,在這裡要使用那個變數.
 (2) static 表示靜態的變數,分配記憶體的時候, 儲存在靜態區,不儲存在棧上面.

    static 作用範圍是內部連線的關係, 和extern有點相反.它和物件本身是分開儲存的,extern也是分開儲存的,但是extern可以被其他的物件用extern 引用,而static 不可以,只允許物件本身用它. 具體差別首先,static與extern是一對“水火不容”的傢伙,也就是說extern和static不能同時修飾一個變;其次,static修飾的全域性變數宣告與定義同時進行,也就是說當你在標頭檔案中使用static聲明瞭全域性變數後,它也同時被定義了;最後,static修飾全域性變數的作用域只能是本身的編譯單元,也就是說它的“全域性”只對本編譯單元有效,其他編譯單元則看不到它,如:
    (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;  }
    以上兩個編譯單元可以連線成功, 當你開啟test1.obj時,你可以在它裡面找到字串"123456",同時你也可以在test2.obj中找到它們,它們之所以可以連線成功而沒有報重複定義的錯誤是因為雖然它們有相同的內容,但是儲存的實體地址並不一樣,就像是兩個不同變數賦了相同的值一樣,而這兩個變數分別作用於它們各自的編譯單元。 也許你比較較真,自己偷偷的跟蹤除錯上面的程式碼,結果你發現兩個編譯單元(test1,test2)的g_str的記憶體地址相同,於是你下結論static修飾的變數也可以作用於其他模組,但是我要告訴你,那是你的編譯器在欺騙你,大多數編譯器都對程式碼都有優化功能,以達到生成的目標程式更節省記憶體,執行效率更高,當編譯器在連線各個編譯單元的時候,它會把相同內容的記憶體只拷貝一份,比如上面的"123456", 位於兩個編譯單元中的變數都是同樣的內容,那麼在連線的時候它在記憶體中就只會存在一份了,如果你把上面的程式碼改成下面的樣子,你馬上就可以拆穿編譯器的謊言:
    (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_str地址並不相同,因為你在一處修改了它,所以編譯器被強行的恢復記憶體的原貌,在記憶體中存在了兩份拷貝給兩個模組中的變數使用。正是因為static有以上的特性,所以一般定義static全域性變數時,都把它放在原檔案中而不是標頭檔案,這樣就不會給其他模組造成不必要的資訊汙染,同樣記住這個原則吧!

7. extern 和const

   C++中const修飾的全域性常量據有跟static相同的特性,即它們只能作用於本編譯模組中,但是const可以與extern連用來宣告該常量可以作用於其他編譯模組中, 如extern const char g_str[];
    然後在原檔案中別忘了定義:     const char g_str[] = "123456"; 

    所以當const單獨使用時它就與static相同,而當與extern一起合作的時候,它的特性就跟extern的一樣了!所以對const我沒有什麼可以過多的描述,我只是想提醒你,const char* g_str = "123456" 與 const char g_str[] ="123465"是不同的, 前面那個const 修飾的是char *而不是g_str,它的g_str並不是常量,它被看做是一個定義了的全域性變數(可以被其他編譯單元使用), 所以如果你像讓char*g_str遵守const的全域性常量的規則,最好這麼定義const char* const g_str="123456".

相關推薦

c++extern關鍵字和static的作用域的學習

之前對static修飾變數後作用域的問題一直是大體瞭解,感覺和普通變數的作用域並沒有什麼區別。聚個栗子 在A.h中: #pragma once static int a=365; class A { public: A(); ~A(); }; 在main.c中: #include

C++extern關鍵字作用

1 基本解釋:extern可以置於變數或者函式前,以標示變數或者函式的定義在別的檔案中,提示編譯器遇到此變數和函式時在其他模組中尋找其定義。此外extern也可用來進行連結指定。       也就是說extern有兩個作用,第一個,當它與"C"一起連用時,如: exte

C/C++extern關鍵字詳解

編譯器 fin 生成 接口 bcd 只需要 c++環境 結束 編程 轉自:http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html 1 基本解釋:extern可以置於變量或者函數前,以標示變量或者

C++static關鍵字作用總結

可見性 apple 局部變量 內存 父類靜態 初始化 ati 退出 變量存儲 1.先來介紹它的第一條也是最重要的一條:隱藏。(static函數,static變量均可) 當同時編譯多個文件時,所有未加static前綴的全局變量和函數都具有全局可見性。舉例來說明。同時編譯兩個源

C++static關鍵字作用

@著作權歸作者所有:來自CSDN部落格作者大鬍子的艾娃的原創作品,如需轉載,請註明出處,否則將追究法律責任。 如有錯誤的地方歡迎指正,謝謝! 一、 static宣告全域性靜態變數和區域性靜態變數 1、static宣告全域性靜態變數 該全域性變數只能供本模組使用,

C#extern作用

C# extern 在方法宣告中使用extern修飾符支援在外部實現方法。 C# extern 在方法宣告中使用extern修飾符支援在外部實現方法。外部修飾符的常見方法是在使用Intero

C++extern關鍵字用法小結

總結C++中關於extern關鍵字的用法。 1.變數的生明和定義中 C++語言支援分離式編譯機制,該機制允許將程式分割為若干個檔案,每個檔案可被獨立編譯。為了將程式分為許多檔案,則需要在檔案中共享程式碼,例如一個檔案的程式碼可能需要另一個檔案中中定義的變數。 為了支援分離式編譯,C++允許將宣告和定義分離開來

C++extern關鍵字使用

chapter 1 . extern關鍵字的作用     extern是一個關鍵字,它告訴編譯器存在著一個變數或者一個函式,如果在當前編譯語句的前面中沒有找到相應的變數或者函式,也會在當前檔案的後面或者其它檔案中定義,來看下面的例子。 // extern.cpp : De

C/C++static關鍵字作用總結(轉)

源地址 C/C++中static關鍵字作用總結 隱藏 先來介紹它的第一條也是最重要的一條:隱藏(static函式,static變數均可) 當同時編譯多個檔案時,所有未加static字首的全域性變數和函式都具有全域性可見性。舉例來說明。同時編譯兩個原始

C++static關鍵字作用

static的作用主要有兩個方面: 限定作用域; 保持變數內容持久化。 static關鍵字在C++中的用法: 只在cpp內有效的全域性變數: 在cpp檔案的全域性範圍內宣告:   static int val = 0; 這個變數的含義是該cpp內有效,

C++explicit關鍵字作用

explicit用來防止由建構函式定義的隱式轉換。   要明白它的作用,首先要了解隱式轉換:可以用單個實參來呼叫的建構函式定義了從形參型別到該類型別的一個隱式轉換。 例如:  

CC#static關鍵字作用

1. static 變數  靜態變數的型別說明符是static。靜態變數當然是屬於靜態儲存方式,但是屬於靜態儲存方式的量不一定就是靜態變數。例如外部變數雖屬於靜態儲存方式,但不一定是靜態變數,必須由static加以定義後才能成為靜態外部變數,或稱靜態全域性變數。2. 靜態區域性變數  靜態區域性變數屬於靜態儲

c#volatile關鍵字作用(zz)

恐怕比較一下volatile和synchronized的不同是最容易解釋清楚的。volatile是變 量修飾符,而synchronized則作用於一段程式碼或方法;看如下三句get程式碼: int i1;              int geti1() {return

C++ explicit 關鍵字作用(轉載) 2012-10-10 14:35

在 C++ 中, 如果一個類有隻有一個引數的建構函式,C++ 允許一種特殊的宣告類變數的方式。在這種情況下,可以直接將一個對應於建構函式引數型別的資料直接賦值給類變數,編譯器在編譯時會自動進行型別轉換,將對應於建構函式引數型別的資料轉換為類的物件。 如果在建構函式前加上 explicit 修飾詞, 則會禁止

c++virtual關鍵字作用與Java多型的一點對比

轉載至:https://segmentfault.com/a/1190000006119157 動機 最近一直在使用C++寫win32程式,用了一些庫,裡面提供的類和demo各種是virtual這個關鍵字,一直不是很明白到底是啥用,於是查看了一些文件,寫小程式來實驗

c#params關鍵字應用

語言 bsp for arr als new 傳參數 lin long c#params應用 params 是C#開發語言中關鍵字, params主要的用處是在給函數傳參數的時候用,就是當函數的參數不固定的時候。 在方法聲明中的 params 關鍵字之後不允許任何其他參數,

C/C++const關鍵字的用法及其與宏定義的比較

類型安全 屬性 const關鍵字 code oid 程序 函數返回值 存儲空間 臨時對象 1.const關鍵字的性質 簡單來說:const關鍵字修飾的變量具有常屬性。 即它所修飾的變量不能被修改。 2.修飾局部變量 1 const int a = 10; 2 int co

c++ explicit 的作用

pre test 一個 調用 顯示 bsp 我們 spa pan 在c++中,只傳一個參數的構造函數定義了一個缺省的轉換操作(隱式轉換),即將該構造函數對應數據類型的數據轉換為該類的對象,如下所示 class A { public: A(int a); } A

淺析C++static關鍵字

參數 extern 函數 iostream 出現 har 兩個 什麽 註意事項 C++的static有兩種用法:面向過程程序設計中的static和面向對象程序設計中的static。前者應用於普通變量和函數,不涉及類;後者主要說明static在類中的作用。 一、面向過程設計中

C++的::的作用【轉】

(轉自:https://blog.csdn.net/zhanghuaichao/article/details/55676209?utm_source=blogxgwz0) (1)作用域限定符,當在類體中直接定義函式時,不需要在函式名字的前面加上類名,但是在類體外實現函式定義的時候,必須加上類名