前言:
先看兩個基礎,函數指針和extern關鍵字,然后由一個具體的例子,具體使用下函數指針。
一、基礎
函數指針:即指向函數的指針,本質還是一個指針。
函數指針的聲明:返回值類型 ( * 指針變量名) ([形參列表]);
注意這里是聲明不是定義,聲明之后它就是一個類型了(與int,char,int *等級別等同,這點有點像結構體),然后就可以定義、使用了。
舉例如下(下面這段小程序摘自百度百科):
int max(int x,int y){return (x>y? x:y);} int main() { int (*ptr)(int, int); int a, b, c; ptr = max; scanf("%d%d", &a, &b); c = (*ptr)(a,b); printf("a=%d, b=%d, max=%d", a, b, c); return 0; }
extern:extern可以置於變量或者函數前,以標示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。此外extern也可用來進行鏈接指定。也就是說extern有兩個作用,第一個,當它與"C"一起連用時,如: extern "C" void fun(int a, int b);則告訴編譯器在編譯fun這個函數名時按着C的規則去翻譯相應的函數名而不是C++的,C++的規則在翻譯這個函數名時會把fun這個名字變得面目全非,可能是fun@aBc_int_int#%$也可能是別的,因為C++支持函數的重載。
第二,當extern不與"C"在一起修飾變量或函數時,如在頭文件中: extern int g_Int; 它的作用就是聲明函數或全局變量的作用范圍的關鍵字,其聲明的函數和變量可以在本模塊活其他模塊中使用,記住它是一個聲明不是定義!也就是說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數時,它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數或變量,但它不會報錯,它會在連接時從模塊A生成的目標代碼中找到此函數。
二、舉例
這里說一下背景,假如我這里做一個平台,我一套代碼要交付到多個產品,然后結合產品代碼進行使用,那么我做平台肯定不能為每個產品做一套代碼,但是有時候同樣一個功能,各個產品之間會出現差異,這個時候指針函數就派上用場了,我給各個產品提供一個指針函數定義的變量,然后各個產品將自己的實現函數掛接在上面,這樣就屏蔽了各個產品的差異,甚至有些產品可以不掛接,那么我就判斷一下,如果沒掛接,我就給一個默認的實現就ok了(這部分在下面代碼中沒體現).
代碼如下:
平台代碼:
#include<iostream> #include"lib_main.h" using namespace std; funcs g_hook_func; void hook_func_init() { g_hook_func.func1=NULL; g_hook_func.func2=NULL; } int main() { char name[10]; int result = 0 ; memset(name,0,sizeof(name)); hook_func_init(); hook_func();//鈎子掛接函數,多線程情況應該在產品側掛接 if(g_hook_func.func1 != NULL) { if(0 == g_hook_func.func1(name)) { cout<<"err"; return -1; } } if(g_hook_func.func2 != NULL) { result = g_hook_func.func2(1,2); } cout<<name<<" "<<result<<endl; return 0; }
平台頭文件:
#ifndef _LIB_MAIN_H_ #define _LIB_MAIN_H_ typedef struct func { int (*func1)(char * str); int (*func2)(int a,int b); }funcs; extern void hook_func(); #endif
產品1代碼:
#include"wlan.h" #include<iostream> using namespace std; static int getname(char * str) { if(NULL == str) { return 0; } //入參大小由調用者保證不越界 str[0]='w'; str[1]='l'; str[2]='a'; str[3]='n'; str[4]='\0'; return 1; } static int add(int a, int b) { return (a+b+3); } void hook_func() { g_hook_func.func1 = getname; g_hook_func.func2 = add; }
產品1頭文件代碼:
#ifndef _WLAN_H_ #define _WLAN_H_ typedef struct func { int (*func1)(char * str); int (*func2)(int a,int b); }funcs; extern funcs g_hook_func; #endif
產品2代碼:
#include"ar.h" #include<iostream> using namespace std; static int getname(char * str) { if(NULL == str) { return 0; } //入參大小由調用者保證不越界 str[0]='a'; str[1]='r'; str[2]='\0'; return 1; } static int add(int a, int b) { return (a+b+1); } void hook_func() { g_hook_func.func1 = getname; g_hook_func.func2 = add; }
產品2頭文件:
#ifndef _AR_H_ #define _AR_H_ typedef struct func { int (*func1)(char * str); int (*func2)(int a,int b); }funcs; extern funcs g_hook_func; #endif
產品3代碼:
#include"fw.h" #include<iostream> using namespace std; static int getname(char * str) { if(NULL == str) { return 0; } //入參大小由調用者保證不越界 str[0]='f'; str[1]='w'; str[2]='\0'; return 1; } static int add(int a, int b) { return (a+b+2); } void hook_func() { g_hook_func.func1 = getname; g_hook_func.func2 = add; }
產品3頭文件:
#ifndef _FW_H_ #define _FW_H_ typedef struct func { int (*func1)(char * str); int (*func2)(int a,int b); }funcs; extern funcs g_hook_func; #endif
說明:
1、上面的所有代碼不能同時運行,想一下也應該知道,應該是一套lib和一套產品放在一個工程下運行。
2、多線程條件下掛接鈎子的函數hook_func應該在產品側掛接,這樣即使沒有掛接,在lib側也沒有影響。
3、平台和產品側的結構都要進行聲明,且要一致typedef struct func { int (*func1)(char * str); int (*func2)(int a,int b); }funcs;
注意這里是聲明,不是定義,所以不會分配內存,聲明只是表示我這里現在有了這種類型(就像是說我這里有一個int一樣)