首頁  >  文章  >  php教程  >  extern用法總結

extern用法總結

高洛峰
高洛峰原創
2016-12-19 14:36:251334瀏覽

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" (?glb@@3HA) already defined in incl1.obj
Debug/incl.exe : fatal error LNK1169: one or more multiply defined symbols founds 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聲明該變量,或者在頭文件中用extern聲明該變量;

對函數而言,如果你想在本源文件中使用另一個源文件的函數,就需要在使用前用聲明該變量,聲明函數加不加extern都沒關係,所以在頭文件中函數可以不用加extern 。


聲明(Declaration)用於說明每個標識符的含義,而並不需要為每個標識符預先儲存空間。預留儲存空間的聲明稱為定義(Definition)。聲明的形式為:聲明說明符聲明符聲明符是由儲存類別說明符和類型說明符組成的。

1、變數的宣告有兩種情況: 一種是需要建立儲存空間的。
例如:int a 在聲明的時候就已經建立了儲存空間。
2、另一種是不需要建立儲存空間。
例如:extern int a 其中變數a是在別的檔案中定義的。

例一:
Declaration.
A construct which associates attributes to a variable name or function. No storage is reserved. For example:
extrn int auredips ext char cration ext.
Definition.
Variable definition is a declaration with storage allocation.


struct per_rec

int age;

char*surname; ;
char c;
struct per_rec person;

   

A construct which specifies the name,parameters and return type of a function. For example a function definition would be: );

}

    


前者是"定義性聲明(defining declaration)"或稱為"定義(definition)",而後者是"引用性聲明(referncing declaration)" 。從廣義的角度來講 聲明中包含著定義,但是並非所有的聲明都是定義,例如:int a 它既是聲明,同時又是定義。然而對於 extern a 來講它只是聲明不是定義。它可以在同一原始程式或不同的來源程式中重複聲明。一般的情況下我們常常這樣敘述,把建立空間的聲明稱之為"定義",而把不需要建立儲存空間稱之為"聲明"。很明顯我們在這裡指的聲明是範圍比較窄的,也就是說非定義性質的聲明。
例如:在主函數中

int main()

int a; //這裡是定義(分配空間的聲明),它不能重複出現

//這裡若寫extern int a;或int a;在VC6.0中編譯均報錯重複定義

  //(redefinition)

  //這裡寫int a;在DEV-C++中編譯錯誤重複聲明(redeclaration)
  //這裡寫extern int a;在DEV-C++中編譯、運行無問題
tern A; //這是個宣告而不是定義,宣告A是一個已經定義了的外部變數
  //注意:宣告外部變數時可以把變數類型去掉如:extern A;
dosth(); //執行函數

int A;   //是定義,定義了A為整數的外在變數

   

看來定義或宣告有時與編譯器的處理也有關係。

外部變數的"定義"與外部變數的"聲明"是不相同的,外部變數的定義只能有一次,它的位置是在所有函數之外,而同一個檔案中的外部變數宣告可以是多次的,它可以在函數之內(哪個函數要用就在那個函數中聲明)也可以在函數之外(在外部變數的定義點之前)。系統會根據外部變數的定義(而不是根據外部變數的聲明)分配儲存空間的。對於外部變數來講,初始化只能是在"定義"中進行,而不是在"聲明"中。
所謂的"聲明",其作用,是聲明該變量是一個已在後面定義過的外部變量,僅僅是在為了"提前"引用該變量而作的"聲明"而已。 extern 只作聲明,不作任何定義 。

用static來宣告一個變數的作用有二:
(1) 對於局部變數用static聲明,則是為此變數所分配的空間在整個程式的執行期間內總是存在。
(2) 外在變數以static來聲明,則變數的作用僅限於本檔案模組。

#include "stdafx.h"
1.extern用在變量聲明中常常有這樣一個作用,你在*.c文件中聲明了一個全局的變量,這個全局的變量如果要被引用,就放在*.h中並用extern來聲明。
2.如果函數的聲明中帶有關鍵字extern,只是暗示這個函數可能在別的源文件裡定義,沒有其它作用。即下述兩個函數宣告沒有差異:
extern int f(); 和int f();
================== ===========
如果定義函數的c/cpp檔案在對應的頭檔中聲明了定義的函數,那麼在其他c/cpp檔中要使用這些函數,只需要包含這個頭檔即可。
如果你不想包含頭文件,那麼在c/cpp中聲明該函數。一般來說,聲明定義在本文件的函數不用“extern”,聲明定義在其他文件中的函數用“extern”,這樣在本文件中調用別的文件定義的函數就不用包含頭檔
include “*.h”來聲明函數,聲明後直接使用即可。
=====================例:
//extern. cpp內容如下:

// extern.cpp : Defines the entry point for the console application.
//

*p);
int main(int argc, char* argv[])
{
 char *p="hello world!";
 print(p);
;
}
//print.cpp內容如下
#include "stdafx.h"
#include "stdio.h"
print( printf("The string is %sn",s);
}
結果程式可以正常運行,輸出結果。如果把「extern」去掉,程式還是可以正常運作。

由此可見,「extern」在函數宣告中可有可無,只是用來標誌該函數在本文件中定義,還是在別的文件中定義。只要你函數在使用之前就聲明了,那麼就可以不用包含頭檔了。

在C語言中,修飾符extern用在變數或函數的宣告前,用來說明「此變數/函數是在別處定義的,要在此處引用」。

0. extern修飾變數的宣告。舉例來說,如果檔案a.c需要引用b.c中變數int v,就可以在a.c中宣告extern int v,然後可以引用變數v。這裡要注意的是,被引用的變數v的連結屬性必須是外連結(external)的,也就是說a.c要引用到v,不只是取決於在a.c中宣告extern int v,還取決於變數v本身是能夠被引用到的。這牽涉到c語言的另一個主題-變數的作用域。能夠被其他模組以extern修飾符引用到的變數通常是全域變數。還有很重要的一點是,extern int v可以放在a.c中的任何地方,例如你可以在a.c中的函數fun定義的開頭處聲明extern int v,然後就可以引用到變數v了,只不過這樣只能在函數fun作用域引用v罷了,這還是變數作用域的問題。對這一點來說,很多人使用的時候都心存顧慮。好像extern宣告只能用於檔案作用域似的。

1. extern修飾函數宣告。從本質上來講,變數和函數沒有差別。函數名稱是指向函數二進位區塊開頭處的指標。如果檔案a.c需要引用b.c中的函數,例如在b.c中原型是int fun(int mu),那麼就可以在a.c中宣告extern int fun(int mu),然後就能使用fun來做任何事情。就像變數的宣告一樣,extern int fun(int mu)可以放在a.c中任何地方,而不一定要放在a.c的檔案作用域的範圍中。對其他模組中函數的引用,最常用的方法是包含這些函數聲明的頭檔。使用extern和包含頭檔來引用函數有什麼不同呢? extern的引用方式比包含頭檔要簡潔得多! extern的使用方法是直接了當的,想引用哪個函數就用extern宣告哪個函數。這大概是KISS原則的一種體現吧!這樣做的一個明顯的好處是,會加速程式的編譯(確切的說是預處理)的過程,節省時間。在大型C程式編譯過程中,這種差異是非常明顯的。

2. 此外,extern修飾符可用於指示C或C++函數的呼叫規格。例如在C++中呼叫C函式庫函數,就需要在C++程式中用extern “C”宣告要引用的函數。這是給連結器的,告訴連結器在連結的時候用C函數規格來連結。主要原因是C++和C程式編譯完成後在目標程式碼中命名規則不同



更多extern用法總結相關文章請關注PHP中文網!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn