extern 作用詳解

extern 作用詳解

轉自:http://blog.csdn.net/songjinshi/article/details/6785267

 

extern 作用1:聲明外部變量
現代編譯器一般採用按文件編譯的方式,因此在編譯時,各個文件中定義的全局變量是
互相透明的,也就是說,在編譯時,全局變量的可見域限制在文件內部。

例1:
創建一個工程,裏面含有A.cpp和B.cpp兩個簡單的C++源文件:
//A.cpp:
int iRI;
int main()
{
//.....
}

//B.cpp
int iRI;

gcc A.cpp -c
gcc B.cpp -c
編譯出A.o, B.o都沒有問題。
但當gcc A.o B.o -o test時,
main.o:(.bss+0x0): multiple definition of `iRI'
b.o:(.bss+0x0): first defined here
報錯:重定義。
(但有個非常意外的發現:當同樣的代碼,使用A.c B.c.並使用gcc編譯時,竟然不會報重定義的錯誤,非常不明白是怎麼回事。)
這就是說,在編譯階段,各個文件中定義的全局變量相互是透明的,編譯A時覺察不到B中也定義了i,同樣,編譯B時覺察不到A中也定義了i。
但是到了鏈接階段,要將各個文件的內容“合爲一體”,因此,如果某些文件中定義的全局變量名相同的話,在這個時候就會出現錯誤,也就是上面提示的重複定義的錯誤。因此,各個文件中定義的全局變量名不可相同。

但如果用下列方式:在B.cpp中定義iRI;在A.cpp中直接使用。則編譯A.cpp時就無法通過。
//A.cpp
int main()
{
iRI=64;
}

//B.cpp
int iRI;

gcc A.cpp -c
was not declared in this scope.


因爲編譯器按照文件方式編譯,所以編譯A.cpp時,並不知道B.cpp中定義了iRI。
也就是說:文件中定義的全局變量的可見性擴展到整個程序是在鏈接完成之後,而在編譯階段,他們的可見性仍侷限於各自的文件。
解決方案如下:
編譯器的目光不夠長遠,編譯器沒有能夠意識到,某個變量符號雖然不是本文件定義的,但是它可能是在其它的文件中定義的。
雖然編譯器不夠遠見,但是我們可以給它提示,幫助它來解決上面出現的問題。這就是extern的作用了。
extern的原理很簡單,就是告訴編譯器:“你現在編譯的文件中,有一個標識符雖然沒有在本文件中定義,但是它是在別的文件中定義的全局變量,你要放行!”
//A.cpp:
extern int iRI;
int main()
{
iRI = 64;
//.....
}

//B.cpp
int iRI;
這樣編譯就能夠通過。
extern int iRI; //並未分配空間,只是通知編譯器,在其它文件定義過iRI。


extern 作用2:在C++文件中調用C方式編譯的函數
C方式編譯和C++方式編譯
相對於C,C++中新增了諸如重載等新特性。所以全局變量和函數名編譯後的命名方式有很大區別。
int a;
int functionA();
對於C方式編譯:
int a;=> _a
int functionA(); => _functionA
對於C++方式編譯:
int a; =>xx@xxx@a
int functionA(); =>
xx@xx@functionA
可以看出,因爲要支持重載,所以C++方式編譯下,生成的全局變量名和函數名複雜很多。與C方式編譯的加一個下劃線不同。
於是就有下面幾種情況:
例2:C++調用C++定義的全局變量
//A.cpp:
extern int iRI;
int main()
{
iRI = 64;
//.....
}
//B.cpp
int iRI;
gcc A.cpp -c
gcc B.cpp -c
gcc A.o B.o -o test
那麼在編譯鏈接時都沒問題。

例3:C++調用C定義的全局變量
//A.cpp:
extern int iRI;
int main()
{
iRI = 64;
//.....
}
//B.c
int iRI;
編譯時沒有問題,
gcc A.cpp -c
gcc B.c -c
但鏈接時,gcc B.o A.o -o test
則會報iRI沒有定義。爲什麼呢?
因爲gcc看到A.cpp,就使用C++方式編譯,看到B.c,就使用C方式編譯。
所以在A.cpp中的iRI=>XXX@XXX_iRI;
而B.c中iRI=〉_iRI;
所以在鏈接時,A.cpp想找到
XXX@XXX_iRI,當然找不到。所以就需要告訴編譯器,iRI是使用C方式編譯的。
//A.cpp:
extern "C"
{
int iRI;
}
int main()
{ iRI = 64;
//.....
}
//B.c
int iRI;
這樣,當編譯A.cpp時,編譯器就知道iRI爲C方式編譯的。就會使用 _iRI。這樣B.c提供的_iRI就可以被A.cpp找到了。

例4:C++調用C定義的function
//A.cpp
extern int functionA();

int main()
{
functionA();
}

//B.c
int functionA()
{
//....
}
gcc A.cpp -c
gcc B.c -c
都沒有問題。但同樣的,gcc A.o B.o -o test
則報錯,找不到functionA();
這是因爲gcc將A.cpp認爲是C++方式編譯,B.c是C方式編譯。
所以functionA在B.c中爲:_functionA. 在A.cpp中爲:
XX@XXX_functionA
所以在鏈接時A.cpp找不到XX@XX_function.
於是需要通知編譯器,functionA()是C方式編譯命名的。
//A.cpp
extern "C"
{
int functionA();
}
int main()
{
functionA();
}

//B.c
int functionA()
{
//....
}
於是,編譯鏈接都可以通過。

總結:

extern "C"
{
functionA();
}//不止是聲明,並且還指出:這個function請用C方式編譯。所以不需要再次extern.
extern"C"
{
extern functionA();
}//這樣做沒什麼太大意義。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章