1. 程式人生 > >extern作用詳解

extern作用詳解

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; =>[email protected]@a
int functionA(); =>
[email protected]@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=>[email protected]_iRI;
而B.c中iRI=〉_iRI;
所以在連結時,A.cpp想找到
[email protected]_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中為:
[email protected]_functionA
所以在連結時A.cpp找不到[email protected]_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();
}//這樣做沒什麼太大意義。

相關推薦

extern作用

extern 作用1:宣告外部變數 現代編譯器一般採用按檔案編譯的方式,因此在編譯時,各個檔案中定義的全域性變數是 互相透明的,也就是說,在編譯時,全域性變數的可見域限制在檔案內部。 例1: 建立一個

extern C的作用

     extern "C"的主要作用就是為了能夠正確實現C++程式碼呼叫其他C語言程式碼。加上extern "C"後,會指示編譯器這部分程式碼按C語言的進行編譯,而不是C++的。由於C++支援函式過載,因此編譯器編譯函式的過程中會將函式的引數型別也加到編譯後的程式碼中,而

Python __init__.py 作用

引用文件 site linu 塊對象 and 語句 inux python url __init__.py 文件的作用是將文件夾變為一個Python模塊,Python 中的每個模塊的包中,都有__init__.py 文件。 通常__init__.py 文件為空,但是我們還可

C/C++中extern關鍵字

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

java虛擬機內存區域的劃分以及作用

define 出口 height 默認值 屬性 字節 一次 計數 有一點       序言          為什麽有時候學著學著會突然之間覺得一切度是那麽無趣,男的每個月也有那麽幾天難道?哈哈,不然是什麽,我還是要堅持,可以做少一點,但是不能什麽度不做。總會過去的,加油

ContextLoaderListener作用

inter ble tle 分隔 name 在一起 vax rabl 自定義文件 ContextLoaderListener的作用後就是:加載spring 的上下文信息,這是個spring的類,但實現了javax的servletcontextListener接口。 Con

jsp 九大內置對象和其作用

tex next 應用服務 java 響應 exceptio servle 輸出流 XML JSP中一共預先定義了9個這樣的對象,分別為:request、response、session、application、out、pagecontext、config、page、exc

Struts2基本包作用

Struts2是的基本包有很多,我們只能記住常用的幾個包的用途,整理了常用的基本包用途詳解。 asm-3.3.jar 作用:操作java位元組碼的類庫 asm-commons-3.3.jar 作用:提供了基於事件的表現形式 asm-tree-3.3.jar

C語言中static關鍵字的作用

在C語言中,static的字面意思很容易把我們匯入歧途,其實它的作用有三條。 (1)先來介紹它的第一條也是最重要的一條:隱藏。 當我們同時編譯多個檔案時,所有未加static字首的全域性變數和函式都具有全域性可見性。為理解這句話,我舉例來說明。我們要同時編譯兩個原始檔,一個是a.c,另一個是

連線池的作用

連線池的作用               連線池的作用:連線池是將已經建立好的連線儲存在池中,當有請求來時,直接使用已經建立好的連線對資料庫進行訪問。這樣省略了建立連線和銷燬連線的過程。這樣效能上

java 中 static 作用

     static表示“全域性”或者“靜態”的意思,用來修飾成員變數和成員方法,也可以形成靜態static程式碼塊,但是Java語言中沒有全域性變數的概念。 被static修飾的成員變數和成員方法獨立於該類的任何物件。也就是說,它不依賴

STM32中stm32f0xx_flash.icf檔案的作用!(不錯的!)

每個晶片開發商都會針對每款晶片來編寫一個.icf檔案就是傳說中的連結檔案。對於基本的應用,這個.icf檔案足以滿足你的工程需要。但有時也會需要改動,比如當你的專案要新增外部RAM時就要修改一下icf。 1、Icf到底幹了啥呢? 定義了晶片儲存空

C++ 函式指標及其作用

查了很多資料,對函式指標已瞭解。 函式指標指向某種特定型別,函式的型別由其引數及返回型別共同決定,與函式名無關。舉例如下: int add(int nLeft,int nRight);//函式定義    該函式型別為int(

Android SDK 目錄和作用

1、add-ons 這裡面儲存著附加庫,比如GoogleMaps,當然你如果安裝了OphoneSDK,這裡也會有一些類庫在裡面。 2、docs 這裡面是Android SDKAPI參考文件,所有的API都可以在這裡查到。 3、market_licensing 作為A

二級指標的作用

一、概念 在如下的A指向B、B指向C的指向關係中: 首先 C是"一段內容",比如你用malloc或者new分配了一塊記憶體,然後塞進去"一段內容",那就是C了。C的起始地址是0x00000008。 B是一個指標變數,其中存放著C的地址,但是B也要佔空間的啊,所以B也有

jsp 九大內建物件和其作用

   JSP中一共預先定義了9個這樣的物件,分別為:request、response、session、application、out、pagecontext、config、page、exception 1、request物件 request 物件是 javax.servlet.httpServletRequ

java中的 break continue return作用

break: 此語句導致程式終止包含它的迴圈,並進行程式的下一階段(整個迴圈後面的語句),即,不是跳到下一個迴圈週期而是退出迴圈。如果break語句包含在巢狀迴圈裡,它只跳出最裡面的迴圈。 如下程式碼: public class Test { public static void main(

Unity3d中特殊資料夾以及作用

1.Editor Editor資料夾可以在根目錄下,也可以在子目錄裡,只要名子叫Editor就可以。比如目錄:/xxx/xxx/Editor  和 /Editor 是一樣的,無論多少個叫Editor的資料夾都可以。Editor下面放的所有資原始檔或者指令碼檔案都不會被打進發

vue-router的routes中name屬性作用

我們常用vue.js和vue-router來建立單頁應用,vue-router能很方便的管理所有的單頁元件。我們在定義每個路由的時候會有一個name的屬性(如下面程式碼),通常我們不定義這個屬性發現也沒有任何問題,那麼這個name到底有什麼用呢? export defa

Mybatis中@Param的用法和作用

用註解來簡化xml配置的時候,@Param註解的作用是給引數命名,引數命名後就能根據名字得到引數值,正確的將引數傳入sql語句中我們先來看Mapper介面中的@Select方法?1234567package Mapper; public interface Mapper {