1. 程式人生 > >C語言外部變數的使用以及erxtern的用法

C語言外部變數的使用以及erxtern的用法

網上有很多帖子問C語言中extern的用法,而且回答的詳細程度各盡不同. 所以我就像寫一篇博文來談談我對extern的看法,不一定十分恰當,只當大家共勉.

變數定義性宣告和引用性宣告

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

前者是“定義性宣告(defining declaration)”或者稱為“定義(definition)”,而後者是“引用性宣告(referncing declaration)”或者簡成為“宣告(Declaration)

”,從廣義的角度來講宣告中包含著定義,即定義是宣告的一個特例,所以並非所有的宣告都是定義

例如:int a; // 它既是宣告,同時又是定義。
然而對於 extern int a;// 來講它只是宣告不是定義。
extern int a = 10;  // 定義一個變數,同時初始化為10,只是定義不再是宣告

注意: 由於C語言中定義變數的預設儲存型別是extern的
因此:
int a = 10;   等價於  extern int a = 10;  //  只是定義不是宣告,但是extern int a = 10;作為定義在gcc下會有警告

一般的情況下我們常常這樣敘述,把建立空間的宣告稱之為“定義”,而把不需要建立儲存空間的宣告稱之為“宣告”。很明顯我們在這裡指的宣告是範圍比較窄的,即狹義上的宣告,也就是說非定義性質的宣告

變數的宣告和定義與extern

在具體到extern的用法之前,有兩個概念必須要能分清楚:
宣告(Declaration)定義(Definition)之間的區別.

宣告一個變數只是宣佈這個變數的屬性,也就是說告訴編譯器這個變數時什麼型別(如int, long, string 等).
定義一個變數不僅是聲明瞭變數的屬性,同時也告訴編譯器給變數分配相應的儲存空間.

extern的用法詳解

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

extern修飾變數的宣告。

舉例來說,如果檔案main.c需要引用extern.c中變數int value,就可以在main.c中宣告extern int value,然後就可以引用變數value。

這裡需要注意的是,被引用的變數v的連結屬性必須是外連結(external)的,也就是說main.c要引用到value,不只是取決於在main.c中宣告extern int value,還取決於變數value本身是能夠被引用到的。這涉及到c語言的另外一個話題--變數的作用域。能夠被其他模組以extern修飾符引用到的變數通常是全域性變數。還有很重要的一點是,extern int value可以放在main.c中的任何地方,比如你可以在main.c中的函式fun定義的開頭處宣告extern int value,然後就可以引用到變數value了,只不過這樣只能在函式fun作用域中引用value罷了,這還是變數作用域的問題。對於這一點來說,很多人使用的時候都心存顧慮。好像extern宣告只能用於檔案作用域似的。
總結起來可以這樣說,宣告只是告訴編譯器宣告的變數和函式是存在的,但並沒有真正分配空間給它,所以當後面的程式碼用到前面宣告的變數或函式時,編譯器在編譯的時候不會報錯。連結的時候編譯器會去尋找這些變數和函式的記憶體地址,如果只聲明瞭但沒定義,連結器當然找不到它們了,所以就會報錯。而對它們進行定義了的話,編譯器就會給它們分配空間,它們就有自己的地址了,這時就能連結了。定義是要分配空間的,且定義只能有一次。而宣告不分配空間,可以宣告多次。

extern修飾函式宣告

從本質上來講,變數和函式沒有區別。函式名是指向函式二進位制塊開頭處的指標。如果檔案main.c需要引用extern.c中的函式,比如在extern.c中原型是int fun(int num),那麼就可以在main.c中宣告extern int fun(int num),然後就能使用fun來做任何事情。就像變數的宣告一樣,extern int fun(int mu)可以放在main.c中任何地方,而不一定非要放在main.c的檔案作用域的範圍中。
對其他模組中函式的引用,最常用的方法是包含這些函式宣告的標頭檔案。

使用extern和包含標頭檔案來引用函式有什麼區別呢?

extern的引用方式比包含標頭檔案要簡潔得多!extern的使用方法是直接了當的,想引用哪個函式就用extern宣告哪個函式。這大概是KISS原則的一種體現吧!這樣做的一個明顯的好處是,會加速程式的編譯(確切的說是預處理)的過程,節省時間。在大型C程式編譯過程中,這種差異是非常明顯的。

extern修飾符可用於指示C或者C++函式的呼叫規範

比如在C++中呼叫C庫函式,就需要在C++程式中用extern “C”宣告要引用的函式。這是給連結器用的,告訴連結器在連結的時候用C函式規範來連結。主要原因是C++和C程式編譯完成後在目的碼中命名規則不同。

總結

關鍵字extern用來宣告一個變數(或函式),並指出它具有外部連結(它的名字在其它檔案裡是可見的)。被extern修飾的變數,其生存期為程式執行的整個過程(它在程式開始執行時被分配記憶體,在程式執行結束時才被收回)。被extern宣告的變數(或函式)將在同一檔案的後續部分定義,或定義在其它的原始檔中。

在C語言中,關鍵字extern用在變數或者函式的宣告前,用來說明“此變數/函式是在別處定義的,要在此處引用”。關鍵字extern用於變數的宣告:

示例

extern.c

/**********************************************************
    > File Name: extern.c
    > Author: GatieMe
    > Mail: [email protected] 
    > Created Time: 2015年03月27日 星期五 16時11分50秒
 *********************************************************/

#include <stdio.h>
#include <stdlib.h>

extern int ex_num = 20; // 作為變數定義會有警告
int num = 30;
int value;
char str[81] = "abcdefg";

main.c

/**********************************************************
    > File Name: main.c
    > Author: GatieMe
    > Mail: [email protected] 
    > Created Time: 2015年03月27日 星期五 16時12分14秒
 *********************************************************/

#include <stdio.h>
#include <stdlib.h>


extern int num;         //  宣告一個外部變數,定義時使用int num = 30;

extern int ex_num;      //  宣告一個外部變數,定義時2使用extern int ex_num = 20;
extern int value;       //  宣告一個外部變數,定義時使用int value;

extern char str[81];

int a;                  //  既是宣告也是定義
extern int b;           //  只是宣告不是定義            
int c = 10;             //  只是定義不是宣告
extern int d = 20;      //  只是定義不是宣告, 但是作為變數定義會有警告


int main(void)
{

    printf("num = %d\n", num);
    printf("ex_num = %d\n", ex_num);
    printf("value = %d\n", value);
    printf("str = %s\n", str);

    printf("a = %d\n", a);
//  printf("%d\n", b);  // ERROR, 連結錯誤,找不到變數定義
    printf("c = %d\n", c);
    printf("d = %d\n", d);

    return EXIT_SUCCESS;
}

Makefile

#Makefile
main:extern.o main.o
    gcc $^ -o [email protected]

extern.o:extern.c
    gcc  -c $^ -o [email protected] 

main.o:main.c
    gcc  -c $^ -o [email protected] 

clean:
    rm extern.o main.o
    rm main