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

出現這種情況的原因:

C中不支持多態,因此就沒有函數重載。使用extern修飾函數時,該函數在調用時才被連接。由於C不支持多態,按照C的編譯方式,兩個函數雖然有不同的參數,但是C卻會把他們編譯成同樣的名字,因此雖然是兩個不同的函數,調用時也不傳遞參數,但是由於是調用時才被連接的,調用時只檢查函數的名字,因此卻能調用到fun(int n)這個函數。

注意一點:C中使用extern修飾函數時,函數在被調用時才被連接。因此要注意兩個函數的聲明是否是一致的。


這個數從哪來的?不知道。這是因爲我們調用了一個需要參數的函數而沒有傳遞參數給它,而這個函數還傻乎乎的以爲傳過了參數,從寄存器裏讀出它的參數就去幹活了。那個寄存器裏是什麼值?誰也說不準。如果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); 其函數類型是等價的,但前一個是定義函數,後一個是聲明函數。
小結
編寫程序,尤其是大型程序時,建議採用上文所述的方法對不同的變量、函數進行必要的聲明、定義。做好這些細節上的事務,可以爲您的編程、調試、移植等帶來很大的方便。

發佈了68 篇原創文章 · 獲贊 15 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章