一、extern的使用方法
下面是《C語言程式設計》中的關於extern的解釋:
在一個來源程式的所有源檔案中,一個外部變數只能在某個檔案中定義一次,而其他檔案可以通過extern聲明來訪問它(定義外部變數的源檔案中也可以包含對該外部變數的extern聲明)。外部變數的定義中必須指定數組的長度,但extern聲明不一定指定數組的長度。
外部變數的初始化只能出現在其定義中。
假設函數push與pop定義在一個檔案中,而變數val與sp在另一個檔案中定義本那個被初始化(通常不太可能這樣組織程式),則需要通過下面這些定義與申明把這些函數與聲明“綁定”在一起:
在檔案file1中:
extern int sp;
extern double val[];
void push(double f) {.......}
double pop(void) {........}
在檔案file2中:
int sp=0;
double val[MAX_SIZE];
由於在file1中的extern申明不僅放在函數的外部,還放在它們的最前面,因此它們適用於該檔案中的所有函數。對於file1,這樣一組聲明就夠了。如果要在同一檔案中先使用、後定義變數sp與val,也需按照這種方式組織檔案。
使用通俗的語言總結extern的使用方法,殼分為下面三種情況:
1) 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聲明只能用於檔案範圍似的。
2) 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程式編譯過程中,這種差異是非常明顯的。
3) 此外,extern修飾符可用於指示C或者C++函數的調用規範。比如在C++中調用C庫函數,就需要在C++程式中用extern “C”聲明要引用的函數。這是給連結器用的,告訴連結器在連結的時候用C函數規範來連結。主要原因是C++和C程式編譯完成後在目標代碼中命名規則不同。這個用法的說明在下篇文章。
下面再歸納解釋一下extern的用法:
在C語言中,修飾符extern用在變數或者函數的聲明前,用來說明“此變數/函數是在別處定義的,要在此處引用”。
1. 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聲明只能用於檔案範圍似的。
2. 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程式編譯過程中,這種差異是非常明顯的。
3. 此外,extern修飾符可用於指示C或者C++函數的調用規範。比如在C++中調用C庫函數,就需要在C++程式中用extern “C”聲明要引用的函數。這是給連結器用的,告訴連結器在連結的時候用C函數規範來連結。主要原因是C++和C程式編譯完成後在目標代碼中命名規則不同。
二、static的使用方法
下面是《C語言程式設計》中的關於static的解釋:
外部的static聲明通常多用於變數,當然,它也可用於聲明函數。通常情況下,函數名字是全域可訪問的,對整個程式的各個部分而言都可見。但是,如果把函數申明為static類型,則該函數名除了對該函數申明所在的檔案可見外,其他檔案都不可見。
static也可用於申明內部變數。static類型的內部變數同自動變數一樣,是某個特定函數的局部變數,只能在該函數中使用,但它與自動變數不同的是,不管其所在的函數是否別調用,它一直存在,而不像自動變數那樣,隨著所在函數的調用和退出而存在和消失。換句話說,static類型的內部變數是一種只能在某個特定函數中使用但一直佔據空間的變數。
使用通俗的說法解釋關於static的三種用法:
1)在函數體,一個被聲明為靜態變數在這一函數被調用過程中維持其值不變。這個變數也成為靜態局部變數。
2)在模組內(但在函數體外),一個被聲明為靜態變數可以被模組內所用函數訪問,但不能被模組外其它函數訪問。它是一個本地的靜態全域變數。
3)在模組內,一個被聲明為靜態函數只可被這一模組內的其它函數調用。那就是,這個函數被限制在聲明它的模組的本地範圍內使用。
三、typedef的使用方法
下面是《C語言程式設計》中的關於typedef的解釋:
從任何意義上上講,typedef申明並沒有建立一個新類型,它只是為某個已存在的類型增加了一個新的名稱而已。typedef聲明也沒有增加任何新的語義,通過這種方式聲明的變數和通過普通聲明方式聲明的具有完全相同的屬性。實際上,typedef類似於#define語句,但由於typedef是由編譯器解釋的,因此它的文本替換功能要超過處理器的能力。例如:
typedef int (*PFI)(char *, char * );
該語句定義了類型PFI是“一個指向函數的指標“,該函數具有兩個char*的參數,傳回值類型為int *.
除了表達方式更簡潔之外,使用typedef還有另外的兩個重要原因。首先,它可以使程式參數化,以提高程式的可移植性。如果typedef聲明的資料類型同機器有關。那麼,當程式移植到其他機器上時,只需要改變typedef類型定義即可。一個經常使用的情況,對於各種不同大小的整形來說,都使用通過typedef定義的類型名,然後,分別為各個不同的宿主機選擇一組合適的short 、int和long類型大小即可。標準庫中有一些例子,例如size_t和ptrdiff_t等。
四、const的使用方法
C語言中const代表著”不可變“,基本和常量一樣不可修改,但是應用情境不一樣。
1) 應用在變數
const char a='A';
a='B'; //錯誤,變數a的值不可以修改。
此時代表變數a值不可改變,任何企圖修改a變數值的語句(例如a=20;)都會報錯。
2) 應用在指標
應用在*左邊
const char *p;
char const *p;
以上兩條語句作用一樣,都是表示指標指向的變數值不可以修改,但指標可以修改。
例如1;
const char *p='A';
char *q;
*p='B'; //錯,指標指向的值不可以修改。
p=q; //對,指標值可以修改
應用在*右邊
char *const *p;
表示指標值(指標指向的位置)不可以改變,但指標指向的值可以改變。
例如2:
複製代碼 代碼如下:
char *const *p='A';
char *q;
*P='b'; // 對,指標指向的值可以修改。
p=q; //錯誤,指標值不可以修改。
總之,const在*左邊,表示指標指向的值不可以修改。const在*右邊,表示指標值(也就是指標指向的位置)不可以修改。
3)應用在函數參數
例如3:strcat(char *a,char const *b),將參數b指向的字串,添加到參數a字串的末尾。
此時,參數*a值可以改變,但是表示參數*b值不可改變