1. 程式人生 > >C語言中宣告和定義的區別——分析extern關鍵詞。

C語言中宣告和定義的區別——分析extern關鍵詞。

一直很迷惑C語言中的宣告和定義的有些實踐中的用法,說迷惑實踐是因為宣告和定義的概念上的區別是很明確的。

定義和宣告的區別(主要針對變數):

定義是要為變數分配儲存空間,還可以在定義的時候為變數指定初始值。在一個程式中,變數有且僅有一次定義。

宣告用於向程式表明變數的型別和名字。定義包括宣告:定義變數時我們聲明瞭變數的型別和名字。可以使用extern關鍵字宣告變數名而定義它。不定義變數的宣告包括變數名,變數型別和變數型別前的關鍵字extern。

宣告的迷惑,我們可不可以對一個區域性變數進行多次宣告,既然只要有了定義,我們就可以進行多次宣告,那麼區域性變數定義之後是否可以多次宣告,個人認為是不能對一個區域性變數進行宣告(除了定義時包含的宣告外,是不能再做宣告的), 因為再次宣告的形式應該是extern 型別 變數名,但是extern本身的作用是針對全部變數宣告的,所以不可以對區域性變數使用extern。

網上有些人說在全域性變數 int a 和extern int a是一樣的,因為編譯器會預設給int a新增extern的,實質上這裡也很迷惑的。首先extern本身的意思是外部的,全域性的意思,與internal對應,internal主要指的是區域性變數,在函式外部定義的變數本身就是全域性的,預設是extern的,可以被其他檔案訪問,但是與extern int a寫法中所含的意思是否一樣還有待深究。

依據百科百度來說int a和extern int a 區別還是很大的,extern 置於函式或變數前,表示變數或函式的定義在別的檔案(也可能本檔案中), 在連結的時候才會解析符號表,暫時不清楚,如果在同一個檔案中先做extern宣告,再定義,符號解析會不會特殊處理,當然如果不想了解太底層的話,可以明確在一個檔案中先extern宣告再定義,與不同檔案中extern宣告和定義的表現是一樣的。

下面對變數先宣告再定義的形式很像函式先定義後宣告(唯一不同點是宣告函式時我們經常省略掉extern關鍵字,是因為函式宣告預設就是extern的),下面的程式碼在gcc下正常輸出b的值。

<pre name="code" class="cpp">#include<stdio.h>
extern int b;
int main(void)
{
        printf("%d\n", b);
	return 0;

}
int b = 4;



我們來分析這三條語句有什麼區別:

//都為全域性變數

int a;

extern int a;

extern int a = 0;

int a直接定義全域性變數,很明確的定義, extern int a僅僅只是宣告,沒有定義的情況下宣告是會報錯的。extern int a = 0定義a, 與int a = 0效果一樣,但是在gcc下使用extern int a = 0和int  a = 0還是會有一個小小的卻別,int a= 0是不會報錯或警告,但是使用extern int a = 0會報警告, 但是表現效果是一樣的。

<pre name="code" class="cpp">#include <stdio.h>
extern int a = 5;

int main(void)
{
      printf("%d\n", a);
      return 0;
}
//<pre name="code" class="cpp"><span style="background-color: rgb(255, 255, 255);">警告資訊: </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">warning: 'a' initialized and declared 'extern' [enabled by default] </span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">extern int a = 5;</span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">
</span>
<pre name="code" class="cpp">//但是下面extern int a = 0和int a = 0;出現了錯誤資訊:
<pre name="code" class="cpp"><pre name="code" class="cpp">#include <stdio.h>
extern int a = 5;

int main(void)
{
      printf("%d\n", a);
     return 0;
}
int a = 0;



warning: 'a' initialized and declared 'extern' [enabled by default] 

extern int a = 5;

error: redefinition of 'a'

int a = 0

note: previous definition of 'b' was here

extern int a= 5;

在給讀者看這樣一段程式碼:

#include <stdio.h>
extern int a = 5;

int main(void)
{
      printf("%d\n", a);
     return 0;
}
int a;

以我們上面的分析, 編譯時應該出現變數a redifition(重定義)的錯誤,但編譯結果出來時,可以有些初學者會很迷惑居然沒有報錯,只是對extern int a = 5進行了警告。為什麼會與我們上面分析的結果不同呢,其實這裡涉及到了C語言的另一種機種,強符號與弱符號,強符號和弱符號都是針對全域性變數而定,何謂強符號,何謂弱符號,強符號是定義的時候初始化,弱符號定義時未初始化,在C語言中對於相同變數進行定義,要求只能存在一個強符號,允許弱符號的存在,所有上面程式碼中兩句全域性變數的宣告int a = 0;

int a;也是不會報錯的,關於強符號和弱符號的詳細知識可以參詳《程式設計師自我修養》關於強符號和弱符號章節。


通過上面的分析,我們總結如下:

1、區域性變數基本就是定義時包含宣告,應該沒有定義後還有宣告的情況(至少我自己想不到特殊的情況,因為宣告是需要使用extern的,而extern是針對全域性變數的)

1、extern是針對全域性變數和全域性函式的。

2、非static全域性函式預設是extern的(為什麼要把static有extern區分開, 有說法是static和extern是水後不相容的,也就是說extern是可以被外部檔案訪問,但是static相反會將訪問限制在本檔案中)

3、在我們讀程式碼的時候全域性變數int a = 0 和 extern int a = 0表現效果是一樣的,但是對僅僅是extern int a這樣的宣告,一定要在其他檔案或本檔案的其他地方存在定義,也就是做最後符號連結時一定要有其他地方進行定義。

讀者可以對比函式和全域性變數的extern性質,他們是很相似的,它們之間一個大的區別應該是exter int tt和int tt, 其中tt既可以表示函式名也可表示變數名,對於函式來說都是宣告,因為預設加extern,但是對於變數來說無extern時定義,加extern是宣告。

看一下,我隨機想出來的幾個變數宣告和定義,是否合理正確:

int a;
int main()
{
	int a;
}
分析:正確,但是涉及的作用域覆蓋問題,main函式內的變數a和全域性變數a是不同的變數。

----------------------------------

int a;
int main()
{
	exteran int a;
}


分析:正確,extern int a宣告的變數a就是全域性變數定義的a,其實在main裡面不作宣告也是可以直接使用全域性變數a的,這裡面也是因為作用域的問題。

----------------------------------

extern int a;
int main()
{	
	int a;
}

分析:是否通過主要看a是否在其他檔案中有定義,因為extern int a只是做了宣告,而main函式裡的變數a其實與extern int a宣告的變數a是沒有關係的。

-----------------------------------

extern int a
int main()
{
	extern int a;
}


分析:是否通過主要看a是否在其他檔案中有定義,extern int a宣告的變數a就是全域性變數宣告的a,其實在main裡面不作宣告也是可以直接使用全域性變數a的,這裡面也是因為作用域的問題。

分析下面語句的正確性(主要有自己的分析過程,下面都是正確的):

extern int pi = 4;當這條宣告位於函式外部時,並且含有初始化式時,才算定義了pi, 並分配了儲存空間。但是在函式體內則報錯。extern是外部的意思,外部變數只能定義在函式外部。

extern應用場合:任何在多個檔案中使用的變數都需要與定義分離的宣告。在這種情況下,一個檔案含有變數的定義,使用該變數的其他檔案則包含該變數的宣告(而不是定義), 因為變數須且僅能定義一次,而變數的宣告可以多次。