from:https://blog.csdn.net/z702143700/article/details/46805241
一、 extern做變量聲明
l? 聲明extern關鍵字的全局變量和函數可以使得它們能夠跨文件被訪問。
我們一般把所有的全局變量和全局函數的實現都放在一個*.cpp文件里面,然后用一個同名的*.h文件包含所有的函數和變量的聲明。如:
- /*Demo.h*/??
- #pragma??once??
- extern?int a;??
- extern?int b;??
- int add(int a,int b);??
- /*Demo.cpp*/??
- #include?"Demo.h"?/*這句話寫或者不寫在本例中都行,不過建議不寫*/??
- /*不寫不會出問題,寫了有些情況下會出問題,下面有解釋*/??
- int?a?=10;??
- int?b?=20;??
- ???
- int?add(intl,intr)??
- {??
- ??????return?l?+r;??
- }??
如果將Demo.cpp寫成了Demo.c,編譯器會告訴你說無法解析的外部符號。
因為Demo.c里面的實現會被C編譯器處理,然而C++和C編譯器在編譯函數時存在差異,所以會存在找不到函數的情況。
?
l? 全局函數的聲明語句中,關鍵字extern可以省略,因為全局函數默認是extern類型的。
l 聲明和定義
extern?int?a; //屬于聲明??extern?int?a = 10; //屬于定義,同下
extern?char?g_str[]="123456";//這個時候相當于沒有extern
如果在一個文件里定義了char g_str[] = "123456";在另外一個文件中必須使用extern char g_str[ ];來聲明。不能使用extern char* g_str;來聲明。extern是嚴格的聲明。且extern char* g_str只是聲明的一個全局字符指針。
注:聲明可以拷貝n次,但是定義只能定義一次。
二、extern “C”
l? extern "C" 包含雙重含義,從字面上即可得到:首先,被它修飾的目標是“extern”的;其次,被它修飾的目標是“C”的。
被extern "C"限定的函數或變量是extern類型的:
extern是C/C++語言中表明函數和全局變量作用范圍(可見性)的關鍵字,該關鍵字告訴編譯器,其聲明的函數和變量可以在本模塊或其它模塊中使用。記住,下列語句:
extern int a;
僅僅是一個變量的聲明,其并不是在定義變量a,并未為a分配內存空間。變量a在所有模塊中作為一種全局變量只能被定義一次,否則會出現連接錯誤。
通常,在模塊的頭文件中對本模塊提供給其它模塊引用的函數和全局變量以關鍵字extern聲明。
例如,如果模塊B欲引用該模塊A中定義的全局變量和函數時只需包含模塊A的頭文件即可。這樣,模塊B中調用模塊A中的函數時,在編譯階段,模塊B雖然找不到該函數,但是并不會報錯;它會在連接階段中從模塊A編譯生成的目標代碼中找到此函數。
與extern對應的關鍵字是static,被它修飾的全局變量和函數只能在本模塊中使用。因此,一個函數或變量只可能被本模塊使用時,其不可能被extern “C”修飾。
實現C++與C及其它語言的混合編程:
參考:https://blog.csdn.net/gobitan/article/details/1532769
被extern"C"修飾的變量和函數是按照C語言方式編譯和連接的,未加extern “C”則按照聲明時的編譯方式。
l? extern "C"的慣用法
(1)“C++使用C”在C++中引用C語言中的函數和變量,在包含C語言頭文件(假設為cExample.h)時,需進行下列處理:
extern "C"
{
#include "cExample.h" //C++中使用C的函數和變量
}
而在C語言的頭文件中,對其外部函數只能指定為extern類型,C語言中不支持extern"C"聲明,在.c文件中包含了extern"C"時會出現編譯語法錯誤。
(2)“C使用C++”在C中引用C++語言中的函數和變量時,C++的頭文件需添加extern"C",但是在C語言中不能直接引用聲明了extern"C"的該頭文件,應該僅將C文件中將C++中定義的extern"C"函數聲明為extern類型。
三、 extern 和static
(1)extern表明該變量在別的地方已經定義過了,在這里要使用那個變量。
(2)static?表示靜態的變量,分配內存的時候,存儲在靜態區,不存儲在棧上面。
static作用范圍是內部連接的關系這和extern有點相反。它和對象本身是分開存儲的,extern也是分開存儲的,但是extern可以被其他的對象用extern引用,而static不可以,只允許對象本身用它。具體差別首先,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;??
- }??
- /*main.cpp*/??
- void?main()??
- {??
- ??????fun1();//?a23456??
- ??????fun2();//?123456??
- }??
這個時候你在跟蹤代碼時,就會發現兩個編譯單元中的g_str地址并不相同,因為你在一處修改了它,所以編譯器被強行的恢復內存的原貌,在內存中存在了兩份拷貝給兩個模塊中的變量使用。正是因為static有以上的特性,所以一般定義static全局變量時,都把它放在原文件中而不是頭文件,這樣就不會給其他模塊造成不必要的信息污染,同樣記住這個原則吧!
四、extern和const
C++中const修飾的全局常量具有跟static相同的特性,即它們只能作用于本編譯模塊中,且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"。