程式包含指令段和資料段,各自占用儲存空間,有相應位址。函式可看作一組代表指令的二進位制數集合,因此也能用指標指向和訪問。函式指標就是指向可執行**段中的某函式入口位址的指標,通過它可以把函式當成普通資料那樣儲存訪問甚至移動拷貝。
函式指標定義為:返回值型別(*指標變數名)(形參列表);如:int (*f)(int x);注意形似的int *f(int x)代表返回值為指標的普通函式。函式指標的特殊在於::對它取*號代表呼叫指標所指函式,而不是取值,例如:
typedef void (*pfunc) ( ); //定義乙個無引數、無返回型別的函式指標型別
pfunc pf_rst = (pfunc)0; // 函式指標指向0位址,假設為cpu啟動後首條指令位置
*pf_rst(); // 呼叫函式,重啟
這裡沒有定義函式,但只要系統在0位址處預先載入重啟**,*pf_rst()就可以"軟重啟",跳轉到上電首條指令。這種指向常量位址的函式指標是特例,因為c函式名本身就代表函式首位址,所以通常把定義好的函式名做為右值賦給函式指標,如:
int func(int x); // 定義乙個函式
int (*f) (int x); //宣告乙個函式指標
f = func; // 將func函式的首位址賦給指標f
(*f)(x); // 通過函式指標呼叫函式
賦值時作為右值的函式名func不需帶括號和引數,賦值後指標f指向函式func(x)的**首位址。不過函式名和函式指標之間不能任意賦值,須滿足三個條件: a)
具有完全相同的函式引數,包括引數數目和引數型別; b)
具有相同的函式返回值; c)
具有相同的函式呼叫規範,呼叫規範可看做函式型別的一部分(可上網查),規範不相容的函式和函式指標間不能賦值,如:
__stdcall int callee(int);
__cdecl int(*p)(int) = callee; // 錯,指標p和callee()的呼叫規範不同,不能賦值
換句話,函式指標不能與任何型別進行轉換,只能同型別賦值。
1)乙個介面,多種實現
函式指標可為模組間靈活配置轉接介面,使軟體更"軟":根據不同條件選擇不同函式實現,賦給統一的函式指標,這個指標對外提供唯一介面,這是指標高階靈活的應用。
很多程式裡init介面功能之一就是初始化函式指標,從多個版本中通過為函式指標賦值選擇函式實現,在保持介面一致性的同時實現了函式功能動態(執行時)選擇。比如(偽碼):
void (*fir)(int16 *pcoef);
init()
} 這裡函式指標void (*fir)(int16 *pcoef)可根據不同硬體平台選擇不同彙編優化函式,從而提高軟體的多平台相容性。可見:有了函式指標,就具備中間變換的餘地,可構造中間適配層,根據外部輸入讓函式指標在執行時指向適當的函式,上層保持唯一不變的介面,這樣整個系統結構更清晰,具有更好的可擴充套件性。
2)先呼叫後實現,自頂向下的模組化設計
此外一些大型軟體c模組間的介面呼叫除了用函式指標代替直接函式呼叫,還使用dlopen/dlsym方式實現漸進式開發,比如:
libptr = dlopen("lib_functest.so",rtld_now);
*(void **)&(fp_test1) = dlsym(libptr , "func_test1");
*(void **)&(fp_test2) = dlsym(libptr , "func_test2");
......
if(fp_test1) *f_test();
......
初看似乎多此一舉,然而當系統規模巨大時,任何將模組切割出來的方法都有重要意義:
a. 首先用dlopen可選擇性載入庫,比如手機中試圖呼叫某解碼庫時用函式指標和動態庫就可以很靈活:
libmpeg4dec_lib = dlopen("lib_hwdec.so",rtld_now); //先優先嘗試開啟硬體解碼庫
if(libmpeg4dec_lib ==null) //如果不存在
libmpeg4dec_lib = dlopen("lib_swdec.so",rtld_now); //轉而選擇軟體解碼庫。
b. 其次如果庫中不存在某函式,也可進行判斷提示,後續逐漸完善,不影響整體程式執行。即:
if(!(fp_test1=dlsym(libptr, "func_test1"))
想象下如果直接使用函式呼叫func_test1(),而其在lib_fuctest庫中不存在,那只能卡死在編譯錯誤了。函式指標和動態載入庫可以使上層在底層函式不存在的情況下,規劃完成系列介面呼叫,而介面實現可在之後逐步實現。這就把
大系統「分解」成小模組,自上向下實現。在軟體模組和硬體驅動程式的測試程式裡,這種方式經常使用。
總結
函式指標是一種軟編碼方式,使函式呼叫時跳轉的目標位址為變數,因此能夠在執行時動態選擇和改變函式執行分支;與此對應的直接函式呼叫相當於一種硬編碼,編譯完成後函式跳轉位址就固定為常量。想比函式指標,直接呼叫函式顯得笨拙和僵硬,所以linux驅動等系統軟體中大量使用函式指標,在上層封裝出統一的訪問介面,這體現了函式指標的萬能介面功能。
C語言 指標之函式指標
一 函式指標的概念 二 函式指標的應用 1 呼叫函式 2 將函式的位址作為函式引數傳入其他函式。三 例項演示 函式指標的定義 若在程式中定義了乙個函式,編譯時,編譯器會為函式 分配一段儲存空間,這段空間的起始位址 又稱入口位址 稱為這個函式的指標。與普通變數相同,可以定義乙個指標指向存放函式 的儲存...
函式指標 指標函式 指標的指標 指標陣列
一 函式指標 首先它是乙個指標,只是這個指標指向的是乙個函式。指標變數可以指向變數的位址 陣列 字串 動態分配位址,同時也可指向乙個函式,每個函式在編譯的時候,系統會分配給該函式乙個入口位址,函式名表示這個入口位址,那麼指向函式的指標變數稱為函式指標變數。表示 struct file operati...
指標陣列,陣列指標,指標函式,函式指標
int p 4 指標陣列。是個有4個元素的陣列,每個元素的是指向整型的指標。int p 4 陣列指標。它是乙個指標,指向有4個整型元素的陣列。int func void 指標函式。無參函式,返回整型指標。int func void 表示函式指標,可以指向無參,且返回值為整型指標的函式。右左規則 因為...