1. 程式人生 > >C語言中使用extern修飾函式要注意(轉)

C語言中使用extern修飾函式要注意(轉)

用extern修飾函式是表示被修飾的函式定義在當前檔案外,而該函式用extern宣告型別後就可以被當前檔案呼叫了。使用extern修飾某函式時,對該函式的呼叫是在連線階段才被處理的。下面我們看一段程式碼。

/***********************************************
 * fun.c
 ***********************************************/

#include <stdio.h>

void fun (int n)
{
 printf("%d/n");
}


/***********************************************
 * test.c
 ***********************************************/

extern void fun (void);

int main ()
{
 fun();
 return 0;
}

著兩個檔案能編譯連線到一起嗎?答案是肯定的,而且不會出現警告。那我們執行這個程式看看什麼結果:

C:/> test.exe
2367460

這個數從哪來的?不知道。這是因為我們呼叫了一個需要引數的函式而沒有傳遞引數給它,而這個函式還傻乎乎的以為傳過了引數,從暫存器裡讀出它的引數就去幹活了。那個暫存器裡是什麼值?誰也說不準。如果fun函式的引數是作為指標使用……,啊!麻煩大了!

要解決問題,對於上面的小程式,我們直接衝過去改掉就成了。但是如果你的程式裡有1000個檔案,平均每個檔案5000行(真正大的專案要比這大的多),那你還能一行行檢查下去嗎?如果除錯過程中出了問題而你還不知道是這個原因導致的,如果你的程式很少執行到這個地方,……

所以,正確的方法是防患於未然,寫程式碼是養成良好的習慣。

對於使用gcc的朋友,建議總是使用“-Wall”,“-Wall”是一個細心的祕書,他會及時提醒你程式碼中存在的風險。

儘量避免使用extern修飾函式。一般情況下,如果我們希望某個函式可以被外界使用,就建立一個頭檔案,在其中宣告該函式的型別。哪個程式需要呼叫這個函式就把它的標頭檔案包含進去。這樣一來如果呼叫的地方使用不當,在編譯階段就會提示錯誤,也節省了時間。而且,定義函式和宣告函式是同一個人做的,出錯的機會也就小了。

變數
在將變數前,先解釋一下宣告和定義這兩個概念。宣告一個變數意味著向編譯器描述變數的型別,但並不為變數分配儲存空間。定義一個變數意味著在宣告變數的同時還要為變數分配儲存空間。在定義一個變數的同時還可以對變數進行初始化。
區域性變數通常只定義不宣告,而全域性變數多在原始檔中定義,在標頭檔案中宣告。
區域性變數
在一個函式的內部定義的變數是內部變數,它只在本函式範圍內有效。
自動變數auto
函式中的區域性變數,其預設格式是自動變數型別。例如,在函式體中int b, c=3; 和auto int b, c=3; 是等價的。
自動變數是動態分配儲存空間的,函式結束後就釋放。自動變數如不賦初值,則它的值是一個不確定的值。
靜態區域性變數static
靜態區域性變數是指在函式體內宣告和定義的區域性變數,它僅供本函式使用,即其他函式不能呼叫它。靜態區域性變數的值在函式呼叫結束後不消失而保留原值,即其佔用的儲存單元不釋放,在下一次函式呼叫時,該變數已有值,就是上一次函式呼叫結束時的值。
靜態區域性變數在靜態儲存區分配儲存單元,在程式的整個執行期間都不釋放。靜態區域性變數是在編譯時賦初值的,即只賦初值一次。
在SDT編譯器中,建議對靜態區域性變數賦初值,否則該靜態區域性變數的初值為不確定值。在其他編譯器中,未初始化的靜態區域性變數的初值可能為零,這由具體的編譯器所決定,使用前最好測試一下。
暫存器變數register
帶register修飾符的變數暗示(僅僅是暗示而不是命令)編譯程式本變數將被頻繁使用,如果可能的話,應將其保留在CPU的暫存器中,以加快其存取速度。
對於現有的大多數編譯程式,最好不要使用register修飾符。因為它是對早期低效的C編譯程式的一個很有價值的補充。隨著編譯程式技術的進步,在決定哪些變數應當被存到暫存器中時,現在的C編譯程式能比程式設計師做出更好的決定。
全域性變數
在函式之外定義的變數稱為外部變數,外部變數是全域性變數,它可以為本檔案中其他函式所共用。全域性變數都是靜態儲存方式,都是在編譯時分配記憶體,但是作用範圍有所不同。
靜態外部變數static
靜態外部變數只能在本檔案中使用。所以靜態外部變數應該在當前原始檔中宣告和定義。
外部變數extern
定義函式中的全域性變數時,其預設格式是外部變數型別。外部變數應該在一個頭檔案中宣告,在當前原始檔中定義。外部變數允許其他檔案引用。
下例聲明瞭一個變數和一個結構,定義了兩個變數,其中一個定義帶初始化:
extern int   decl1;   // this is a declaration

struct decl2
{
  int member;
};       // this just declares the type – no variable mentioned

int     def1 = 8;   // this is a definition

int     def2;   // this is a definition
函式
內部函式的宣告和定義多在當前原始檔中完成;而外部函式通常在原始檔中定義,在標頭檔案中宣告。
內部函式
只在當前原始檔中使用的函式應該說明為內部函式。內部函式應該在當前原始檔中宣告和定義。若內部函式在標頭檔案中宣告,其他原始檔通過包含這個標頭檔案也可使用這個函式,但這樣就失去了其做為內部函式的意義。
優點:使用內部函式,可以使函式只侷限於所在檔案。這避免了與其他原始檔中可能出現的同名函式發生衝突。
例:
File: function1.c
#include “function1.h”
static int stat_func(void);

void MasterFunction(void)
{

rc = stat_func( );

}

static int stat_func(void)
{
  …
  return rc;
}

外部函式
對於可在當前原始檔以外使用的函式,應該在一個頭檔案中宣告。其他原始檔可通過包含這個標頭檔案或進行宣告來使用這些函式(推薦用前者)。
一個良好的程式設計習慣是在標頭檔案中宣告函式的原型。這可方便編譯程式查錯。定義函式時,預設的函式型別是外部函式。如:void fun2(void); 和extern void fun2(void); 其函式型別是等價的,但前一個是定義函式,後一個是宣告函式。
小結
編寫程式,尤其是大型程式時,建議採用上文所述的方法對不同的變數、函式進行必要的宣告、定義。做好這些細節上的事務,可以為您的程式設計、除錯、移植等帶來很大的方便。