基本理解
extern放在變量或者函數之前,表示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。
extern有兩個作用
1.當它與"C"一起連用時,如: extern "C" void fun(int a, int b);告訴編譯器在編譯fun這個函數名時按着C的規則去翻譯相應的函數名而不是C++的,C++的規則在翻譯這個函數名時會把fun這個名字變得面目全非,可能是fun@aBc_int_int#%$也可能是別的(不同編譯器不同),因為C++支持函數的重載。(詳細轉到我另一篇博客:https://www.cnblogs.com/WindSun/p/11334182.html)
2.在頭文件中: extern int g_Int; 它的作用就是聲明全局變量或函數的作用范圍的關鍵字,其聲明的函數和變量可以在本模塊或其他模塊中使用,記住它是一個聲明不是定義。也就是說B模塊如果引用A模塊中定義的全局變量或函數時,它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數或變量,但它不會報錯,它會在連接時從模塊A生成的目標代碼中找到此函數。
extern用在變量聲明中常常有這樣一個作用,在*.c文件中聲明了一個全局的變量,這個全局的變量如果要被引用,就放在*.h中並用extern來聲明。
extern的注意事項
(1)extern數組變量
在一個源文件里定義了一個數組:char a[6],在另外一個文件里用下列語句進行了聲明:extern char *a是不可以的,便宜可以通過,但運行時出現錯誤。
原因:指向類型T的指針並不等價於類型T的數組。extern char *a聲明的是一個指針變量而不是字符數組,因此與實際的定義不同,從而造成運行時非法訪問。應該將聲明改為extern char a[ ]。
(2)extern全局變量
如果在一個test1.h頭文件中將全局變量的聲明和定義放在一起,
extern char g_str[] = "123456"; // 這個時候相當於沒有extern
在兩個.cpp文件中都有包含這個.h頭文件,這時候再編譯連接test1.cpp和test2.cpp兩個模塊時,會報連接錯誤,這是因為你把全局變量的定義放在了頭文件之后,test1.cpp這個模塊包含了test1.h所以定義了一次g_str,而test2.cpp也包含了test1.h所以再一次定義了g_str,這個時候連接器在連接test1和test2時發現兩個g_str。如果你非要把g_str的定義放在test1.h中的話,那么就把test2.cpp的代碼中#include "test1.h"去掉 換成在變量定義前面加上extern:extern char g_str[];這個時候編譯器就知道g_str是引自於外部的一個編譯模塊了,不會在本模塊中再重復定義一個出來,但是這樣做非常糟糕,因為你由於無法在test2.cpp中使用#include "test1.h",那么test1.h中聲明的其他函數你也無法使用了,除非也用都用extern修飾,這樣的話你光聲明的函數就要一大串,所以 請記住:只在頭文件中做聲明,真理總是這么簡單。
extern和static
static的全局變量作用域只在本文件中,所以extern和static不能同時修飾一個變量;
extern 表明該變量在別的地方已經定義過了,在這里要使用那個變量.
static 表示靜態的變量,分配內存的時候, 存儲在靜態區,不存儲在棧上面.
extern可以被其他的對象用extern 引用,而static 不可以,只允許對象本身用它.
static修飾的全局變量聲明與定義同時進行,也就是說當你在頭文件中使用static聲明了全局變量后,它也同時被定義.
static修飾全局變量的作用域只能是本身的編譯單元,也就是說它的“全局”只對本編譯單元有效,不會影響到其他的單元.
//test1.h:
#ifndef TEST1H
#define TEST1H
static char g_str[] = "123456";
void fun1();
#endif
//test1.cpp:
#include "test1.h"
void fun1() { cout << g_str << endl; }
//test2.cpp
#include "test1.h"
void fun2() { cout << g_str << endl; }
以上兩個編譯單元可以成功,當你打開test1.obj時,你可以在它里面找到字符串"123456",同時你也可以在test2.obj中找到它們,它們之所以可以連接成功而沒有報重復定義的錯誤是因為雖然它們有相同的內容,但是存儲的物理地址並不一樣,就像是兩個不同變量賦了相同的值一樣,而這兩個變量分別作用於它們各自的編譯單元。如果跟蹤調試上面的代碼,結果你發現兩個編譯單元(test1,test2)的g_str的內存地址相同,於是你下結論static修飾的變量也可以作用於其他模塊,但是我要告訴你,那是你的編譯器在欺騙你,大多數編譯器都對代碼都有優化功能,以達到生成的目標程序更節省內存,執行效率更高,當編譯器在連接各個編譯單元的時候,它會把相同內容的內存只拷貝一份,比如上面的"123456", 位於兩個編譯單元中的變量都是同樣的內容,那么在連接的時候它在內存中就只會存在一份了,如果你把上面的代碼改成下面的樣子,你馬上就可以拆穿編譯器的謊言:
//test1.cpp:
#include "test1.h"
void fun1()
{
g_str[0] = ''a'';
cout << g_str << endl;
}
//test2.cpp
#include "test1.h"
void fun2() { cout << g_str << endl; }
void main()
{
fun1(); // a23456
fun2(); // 123456
}
這個時候你在跟蹤代碼時,就會發現兩個編譯單元中的g_str地址並不相同,因為你在一處修改了它,所以編譯器被強行的恢復內存的原貌,在內存中存在了兩份拷貝給兩個模塊中的變量使用。正是因為static有以上的特性,所以一般定義static全局變量時,都把它放在原文件中而不是頭文件,這樣就不會給其他模塊造成不必要的信息污染。
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"。