1. 程式人生 > >extern使用方法總結!

extern使用方法總結!

一、問題
1、Extern的問題在於不知道這個關鍵詞出現的時候到底是宣告還是定義。

二、詳解
1、函式的宣告extern關鍵詞是可有可無的,因為函式本身不加修飾的話就是extern的。但是引用的時候一樣是需要宣告的。
而全域性變數在外部使用宣告時,extern關鍵詞是必須的,如果變數無extern修飾且沒有顯式的初始化,同樣成為變數的定義,因此此時必須加extern,而編譯器在此標記儲存空間在執行時載入如記憶體並初始化為0。
而區域性變數的宣告不能有extern的修飾,且區域性變數在執行時才在堆疊部分分配記憶體。
2、全域性變數或函式本質上講沒有區別,函式名是指向函式二進位制塊開頭處的指標。而全域性變數是在函式外部宣告的變數。函式名也在函式外,因此函式也是全域性的。
3、

extern int i; //宣告,不是定義
int i; //宣告,也是定義

三、謹記

Extern的問題在於不知道這個關鍵詞出現的時候到底是宣告還是定義。

謹記:宣告可以多次,定義只能一次

函式的宣告extern關鍵詞是可有可無的,因為函式本身不加修飾的話就是extern的。但是引用的時候一樣是需要宣告的。

全域性變數在外部使用宣告時,extern關鍵詞是必須的,如果變數無extern修飾且沒有顯式的初始化,同樣成為變數的定義,因此此時必須加extern,而編譯器在此標記儲存空間在執行時載入如記憶體並初始化為0。

區域性變數的宣告不能有extern的修飾,且區域性變數在執行時才在堆疊部分分配記憶體。

引用性宣告、定義性宣告

強符號、弱符號

出現在linux的gcc連結分析中,可以加深連結的理解。

全域性變數或函式本質上講沒有區別,函式名是指向函式二進位制塊開頭處的指標。而全域性變數是在函式外部宣告的變數。函式名也在函式外,因此函式也是全域性的。

在使用中,要形成一種風格。

標頭檔案

首先說下標頭檔案,其實標頭檔案對計算機而言沒什麼作用,她只是在預編譯時在#include的地方展開一下,沒別的意義了,其實標頭檔案主要是給別人看的。

我做過一個實驗,將標頭檔案的字尾改成xxx.txt,然後在引用該標頭檔案的地方用

#include"xxx.txt"

編譯,連結都很順利的過去了,由此可知,標頭檔案僅僅為閱讀程式碼作用,沒其他的作用了!

不管是C還是C++,你把你的函式,變數或者結構體,類啥的放在你的.c或者.cpp檔案裡。然後編譯成lib,dll,obj,.o等等,然後別人用的時候最基本的gcc hisfile.cpp yourfile.o|obj|dll|lib 等等。
但對於我們程式設計師而言,他們怎麼知道你的lib,dll...裡面到底有什麼東西?要看你的標頭檔案。你的標頭檔案就是對使用者的說明。函式,引數,各種各樣的介面的說明。
    那既然是說明,那麼標頭檔案裡面放的自然就是關於函式,變數,類的“宣告”了。記著,是“宣告”,不是“定義”。
那麼,我假設大家知道宣告和定義的區別。所以,最好不要傻嘻嘻的在標頭檔案裡定義什麼東西。比如全域性變數:

#ifndef _XX_標頭檔案.H
#define _XX_標頭檔案.H
int A;
#endif

    那麼,很糟糕的是,這裡的int A是個全域性變數的定義,所以如果這個標頭檔案被多次引用的話,你的A會被重複定義
    顯然語法上錯了。只不過有了這個#ifndef的條件編譯,所以能保證你的標頭檔案只被引用一次,不過也許還是會岔子,但若多個c檔案包含這個標頭檔案時還是會出錯的,因為巨集名有效範圍僅限於本c原始檔,所以在這多個c檔案編譯時是不會出錯的,但在連結時就會報錯,說你多處定義了同一個變數,

Linking...
incl2.obj : error LNK2005: "int glb" ([email protected]@3HA) already defined in incl1.obj
Debug/incl.exe : fatal error LNK1169: one or more multiply defined symbols found

注意!!!

extern

這個關鍵字真的比較可惡,在宣告的時候,這個extern居然可以被省略,所以會讓你搞不清楚到底是宣告還是定義,下面分變數和函式兩類來說:

(1)變數

尤其是對於變數來說。
extern int a;//宣告一個全域性變數a
int a; //定義一個全域性變數a

extern int a =0 ;//定義一個全域性變數a 並給初值。
int a =0;//定義一個全域性變數a,並給初值,

第四個 等於 第 三個,都是定義一個可以被外部使用的全域性變數,並給初值。
糊塗了吧,他們看上去可真像。但是定義只能出現在一處。也就是說,不管是int a;還是extern int a=0;還是int a=0;都只能出現一次,而那個extern int a可以出現很多次。

當你要引用一個全域性變數的時候,你就要宣告,extern int a;這時候extern不能省略,因為省略了,就變成int a;這是一個定義,不是宣告。

(2)函式
     函式,函式,對於函式也一樣,也是定義和宣告,定義的時候用extern,說明這個函式是可以被外部引用的,宣告的時候用extern說明這是一個宣告。 但由於函式的定義和宣告是有區別的,定義函式要有函式體,宣告函式沒有函式體,所以函式定義和宣告時都可以將extern省略掉,反正其他檔案也是知道這個函式是在其他地方定義的,所以不加extern也行。兩者如此不同,所以省略了extern也不會有問題。
    比如:

int fun(void)
{
return 0;
}

很好,我們定義了一個全域性函式

int fun(void);
我們對它做了個宣告,然後後面就可以用了
加不加extern都一樣
我們也可以把對fun的宣告 放在一個頭檔案裡,最後變成這樣

int fun(void);//函式宣告,所以省略了extern,完整些是extern int fun(void);

int fun(void)
{
return 0;
}//一個完整的全域性函式定義,因為有函式體,extern同樣被省略了。
然後,一個客戶,一個要使用你的fun的客戶,把這個標頭檔案包含進去,ok,一個全域性的宣告。沒有問題。
但是,對應的,如果是這個客戶要使用全域性變數,那麼要extern 某某變數;不然就成了定義了。

總結下:

對變數而言,變數的宣告有兩種情況: 一種是需要建立儲存空間的,不用加extern;2、另一種是不需要建立儲存空間,需要加extern 。如果你想在本原始檔中使用另一個原始檔的變數,就需要在使用前用extern宣告該變數,或者在標頭檔案中用extern宣告該變數;

對函式而言,如果你想在本原始檔中使用另一個原始檔的函式,就需要在使用前用宣告該函式,宣告函式加不加extern都沒關係,所以在標頭檔案中函式可以不用加extern。

extern "C"的用法 
連結指示符extern C
    如果程式設計師希望呼叫其他程式設計語言尤其是C 寫的函式,那麼呼叫函式時必須告訴編譯器使用不同的要求,例如當這樣的函式被呼叫時函式名或引數排列的順序可能
不同,無論是C++函式呼叫它還是用其他語言寫的函式呼叫它,程式設計師用連結指示符linkage directive 告訴編譯器該函式是用其他的程式設計語言編寫的,連結指示符有兩種形式既可以是單一語句single statement 形式也可以是複合語句compound statement 形式。
// 單一語句形式的連結指示符
extern "C" void exit(int);
// 複合語句形式的連結指示符
extern "C" {
int printf( const char* ... );
int scanf( const char* ... );
}
// 複合語句形式的連結指示符
extern "C" {
#include <cmath>
}
    連結指示符的第一種形式由關鍵字extern 後跟一個字串常量以及一個普通的函式,宣告構成雖然函式是用另外一種語言編寫的但呼叫它仍然需要型別檢查例如編譯器會檢查傳遞給函式exit()的實參的型別是否是int 或者能夠隱式地轉換成int 型,多個函式宣告可以用花括號包含在連結指示符複合語句中,這是連結指示符的第二種形式花擴號被用作分割符表示連結指示符應用在哪些宣告上在其他意義上該花括號被忽略,所以在花括號中宣告的函式名對外是可見的就好像函式是在複合語句外宣告的一樣,例如在前面的例子中複合語句extern "C"表示函式printf()和scanf()是在C 語言中寫的,函式因此這個宣告的意義就如同printf()和scanf()是在extern "C"複合語句外面宣告的一樣,當複合語句連結指示符的括號中含有#include 時在標頭檔案中的函式宣告都被假定是用連結指示符的程式設計語言所寫的在前面的例子中在標頭檔案<cmath>中宣告的函式都是C函式連結指示符不能出現在函式體中下列程式碼段將會導致編譯錯誤。
int main()
{
// 錯誤: 連結指示符不能出現在函式內
extern "C" double sqrt( double );
305 第七章函式
double getValue(); //ok
double result = sqrt ( getValue() );
//...
return 0;
}
如果把連結指示符移到函式體外程式編譯將無錯誤
extern "C" double sqrt( double );
int main()
{
double getValue(); //ok
double result = sqrt ( getValue() );
//...
return 0;
}
    但是把連結指示符放在標頭檔案中更合適在那裡函式宣告描述了函式的介面所屬,如果我們希望C++函式能夠為C 程式所用又該怎麼辦呢我們也可以使用extern "C"連結指示符來使C++函式為C 程式可用例如。
// 函式calc() 可以被C 程式呼叫
extern "C" double calc( double dparm ) { /* ... */ }
    如果一個函式在同一檔案中不只被宣告一次則連結指示符可以出現在每個宣告中它,也可以只出現在函式的第一次宣告中在這種情況下第二個及以後的宣告都接受第一個聲
明中連結指示符指定的連結規則例如
// ---- myMath.h ----
extern "C" double calc( double );
// ---- myMath.C ----
// 在Math.h 中的calc() 的宣告
#include "myMath.h"
// 定義了extern "C" calc() 函式
// calc() 可以從C 程式中被呼叫
double calc( double dparm ) { // ...
    在本節中我們只看到為C 語言提供的連結指示extern "C",extern "C"是惟一被保證由所有C++實現都支援的,每個編譯器實現都可以為其環境下常用的語言提供其他連結指示例如extern "Ada"可以用來宣告是用Ada 語言寫的函式,extern "FORTRAN"用來宣告是用FORTRAN 語言寫的函式,等等因為其他的連結指示隨著具體實現的不同而不同所以建議讀者檢視編譯器的使用者指南以獲得其他連結指示符的進一步資訊。

總結 extern “C”

       extern “C” 不但具有傳統的宣告外部變數的功能,還具有告知C++連結器使用C函式規範來連結的功能。 還具有告知C++編譯器使用C規範來命名的功能。

相關推薦

extern使用方法總結

一、問題 1、Extern的問題在於不知道這個關鍵詞出現的時候到底是宣告還是定義。 二、詳解 1、函式的宣告extern關鍵詞是可有可無的,因為函式本身不加修飾的話就是extern的。但是引用的時候一樣是需要宣告的。 而全域性變數在外部使用宣告時,extern關鍵詞

關於垂直居中的方法總結

第一條 方法 ofo 最大 在外 AC web 精確 彈性布局 說明 .center表示要被居中的元素,.wrap 表示要居中的元素的父元素(包含.center元素的元素)。 為了便於理解和敘述同一: 對於文本內容居中的情況,.wrap就是指包含文字的元素(例

爬蟲工程師熬夜寫了這篇文章,關於Python爬蟲的一些方法總結

  爬蟲原理與資料抓取 Requests簡單使用 新增 headers 和 查詢引數                 學習Python中有不明白推薦加入交流群    

spark-2.2.0-bin-2.6.0-cdh5.12.1.tgz 編譯方法總結

菜雞一隻,如果有說錯的地方,還請見諒和指出批評!! 事情是這樣的,想在自己本地部署一個hadoop2.6.0-cdh5.12.1,hive1.1.0-cdh5.12.1,spark-2.2.0-bin-2.6.0-cdh5.12.1的環境,前兩個還是很好找到哪裡下載和部署的! c

list1與list2求交集的方法總結 list1與list2求交集的方法總結

一、有序集合求交集的方法有          a)二重for迴圈法,時間複雜度O(n*n)          b)拉鍊法,時間複雜度O(n) &

list1與list2求交集的方法總結

_id 很多 所有 inf 算法 pos 引擎 二級 cnblogs 一、有序集合求交集的方法有 a)二重for循環法,時間復雜度O(n*n) b)拉鏈法,時間復雜度O(n) c)水平分桶,多線程並行

VueJs $watch()方法總結

<!DOCTYPE html><html><head><meta charset="UTF-8"><title></title><script src="http://unpkg.com/vue/d

新浪微博相簿圖片外鏈限制,圖床不顯示解決方法總結

近期新浪微博抽風,很多站長的圖片都存在微博相簿,導致絕大部分網站的圖片無法顯示,文主的站也不幸淪陷,找了各種方法解決此問題,後來綜合各種說法自己找了個方法。 新浪微博相簿外鏈圖片不顯示的其它一些解決辦法: 1、修改域名:ww1.sinaimg.cn 改為 ww4.sinaimg.cn 不好使; 2、

MATLAB工作空間變數的儲存方法總結,非常實用

對於工作空間中變數的儲存和載入可以使用save和load命令,詳細的使用方法通過help指令獲取(help save,help load)。兩條指令最常用的情況為: 1、% 儲存整個工作空間至指定的mat檔案FILE_PATH_NAME,如E:\workspace.mat % 若已經存在同名的m

UE4 經常編譯後出現黃色警告,處理方法總結一下

1、動畫藍圖經常出現編譯後又黃色警告 由於英文不太好,找了個翻譯軟體翻譯了一下,外加自己不怎麼樣的英語,磕磕盼盼的算是弄明白一些,就是提醒有一些多執行緒呼叫函式或者藍圖節點是不安全的,具體也沒弄明白怎麼個不安全法,這個還得找專業的程式再請教了,這裡就先寫一下怎麼處理:開啟專案設定----選

系統崩潰win10系統修復和資料恢復方法總結

如今網際網路時代,使用Windows10系統的人越來越多了,但隨之而來的是更多人在面對Windows10系統崩潰後的不知所措。 那遇到系統崩潰該怎麼解決呢?今天小編就來分享win 10系統崩潰後的解決方法。 一、win 10系統崩潰解決方法 1、Shift鍵+重啟

自我對Map的使用方法總結與歸納

Map的基礎用法 Map是用來處理鍵值對的。與陣列和list的區別在於,陣列和list都是按照int型別的索引得到相應的元素。而map則是允許使用任意的物件作為索引,來得到與之相對應的物件。索引的物件就是“key”,索引的物件就是“value”。 在map中,鍵和值都是O

javascript字符串方法總結

大小寫 comm 其他 tolower 匹配 一行 ror 運算 轉換 一、單引號字符串內部可以使用雙引號,雙引號字符串內部也可以使用單引號 "hello ‘world‘" ‘welcome "to" js‘ 二、多行和轉義 如果要在單引號字符串的內部,使用單引號(或者

web測試中的測試點和測試方法總結

動態 小數 圖片尺寸 提示信息 方便 margin style 容錯性 字符型 測試是一種思維,包括情感思維和智力思維,情感思維主要體現在一句俗語:思想決定行動上(要懷疑一切),智力思維主要體現在測試用例的設計上。具有了這樣的思想,就會找出更多的bug。 一、輸入框

C# Winform 跨線程更新UI控件常用方法總結(轉)

sum tex ase adc 而是 this obj 出現 turn 出處:http://www.tuicool.com/articles/FNzURb 概述 C#Winform編程中,跨線程直接更新UI控件的做法是不正確的,會時常出現“線程間操作無效: 從不是創建控件的

ECMAScript面向對象(二)——之創建對象方法總結

擴展 console 動態 原型 struct 私有屬性 true asc 一份 創建對象的方法 工廠模式 缺點:無法識別對象的類型,因為根本沒有定義新的對象類型 // 工廠模式創建對象 //定義 function createPerson(name,age,

day2 字符串常用方法總結

mes 一個 並且 lun int() join() eba false 換行符 字符串在Python中是常用的功能,我們知道,字符串在Python中存儲的形式是以字符數組的形式存在,比如"alex"在內存中的存儲形式是:["a","l","e","x"],因為我們

zabbix   監控平臺搭建過程中的報錯與解決方法總結

監控 zabbix 運維自動化1.php option post_max_size 2.php option max_execution_time 3.php option max_input_time 4.php time zone 5.php bcm

C# 各種導出的方法總結

src view str inf object ret temp ksh 驅動程序 第一種:使用 Microsoft.Office.Interop.Excel.dll 首先需要安裝 office 的 excel,然後再找到 Microsoft.Office.Interop.

python os模塊功能和方法總結

isp 通用 工作 相同 使用 結束 所有 erro 大量 1 os.sep 可以取代操作系統特定的路徑分割符 2 os.linesep 字符串給出當前平臺使用的行終止符。例如,Windows使用‘\r\n‘,Linux使用‘\n‘ 而Mac使用‘\r‘。