顧名思義,指針函數即返回指針的函數。其通常定義形式以下:程序員
類型名 *函數名(函數參數表列);
複製代碼
其中,後綴運算符括號「()」
表示這是一個函數,其前綴運算符星號「*」
表示此函數爲指針型函數,其函數值爲指針,即它帶回來的值的類型爲指針,當調用這個函數後,將獲得一個「指向返回值爲…的指針(地址),「類型名」表示函數返回的指針指向的類型」。數組
「(函數參數表列)」
中的括號爲函數調用運算符,在調用語句中,即便函數不帶參數,其參數表的一對括號也不能省略。其示例以下:bash
int *pfun(int, int);
複製代碼
因爲「*」
的優先級低於「()」
的優先級,於是pfun首先和後面的「()」
結合,也就意味着,pfun是一個函數。即:函數
int *(pfun(int, int));
複製代碼
接着再和前面的「*」
結合,說明這個函數的返回值是一個指針。因爲前面還有一個int,也就是說,pfun是一個返回值爲整型指針的函數。 咱們不妨來再看一看,指針函數與函數指針有什麼區別?spa
int (*pfun)(int, int);
複製代碼
經過括號強行將pfun首先與「*」
結合,也就意味着,pfun是一個指針,接着與後面的「()」
結合,說明該指針指向的是一個函數,而後再與前面的int結合,也就是說,該函數的返回值是int。因而可知,pfun是一個指向返回值爲int的函數的指針。指針
雖然它們只有一個括號的差異,可是表示的意義卻大相徑庭。函數指針的自己是一個指針,指針指向的是一個函數。指針函數的自己是一個函數,其函數的返回值是一個指針。code
用函數指針做爲指針函數的返回值 在上面提到的指針函數裏面,有這樣一類函數,它們也返回指針型數據(地址),可是這個指針不是指向int、char之類的基本類型,而是指向函數。對於初學者,別說寫出這樣的函數聲明,就是看到這樣的寫法也是一頭霧水。好比,下面的語句:對象
int (*ff(int))(int *, int);
複製代碼
咱們用上面介紹的方法分析一下,ff首先與後面的「()」
結合,即:blog
int (*(ff(int)))(int *, int);
複製代碼
用括號將ff(int)
再括起來也就意味着,ff
是一個函數。 接着與前面的「*」
結合,說明ff
函數的返回值是一個指針。而後再與後面的「()」
結合,也就是說,該指針指向的是一個函數。ip
這種寫法確實讓人很是難懂,以致於一些初學者產生誤解,認爲寫出別人看不懂的代碼才能顯示本身水平高。而事實上剛好相反,可否寫出通俗易懂的代碼是衡量程序員是否優秀的標準。通常來講,用typedef關鍵字會使該聲明更簡單易懂。在前面咱們已經見過:
int (*PF)(int *, int);
複製代碼
也就是說,PF是一個函數指針「變量」。當使用typedef聲明後,則PF就成爲了一個函數指針「類型」
,即:
typedef int (*PF)(int *, int);
複製代碼
這樣就定義了返回值的類型。而後,再用PF做爲返回值來聲明函數:
PF ff(int);
複製代碼
平時咱們在OC中的使用寫法,可是對typedef
困惑。
typedef <#returnType#>(^<#name#>)(<#arguments#>);//typedefBlock Code Snippets
typedef void (^RWAlertViewCompletionBlock)(UIAlertView *alertView, NSInteger buttonIndex);
複製代碼
而後能夠經過RWAlertViewCompletionBlock
當成block類型直接使用了。 而後看下libffi
的用法:
typedef enum {
FFI_OK = 0,
FFI_BAD_TYPEDEF,
FFI_BAD_ABI
} ffi_status;
typedef int INT64;//INT64 實際上是int
複製代碼
使用起來的話:
ffi_status status;//聲明一個類型是ffi_status的參數
INT64 age;//聲明一個age 類型是INT64
複製代碼
如今block也能夠是函數指針了
int (*PF)(int *, int);
//也就是說,PF是一個函數指針「變量」。當使用typedef聲明後,則PF就成爲了一個函數指針「類型」,即:
typedef int (*PF)(int *, int);
複製代碼
typedef的語法規則其實很簡單,一句話來講就是定義對象的語法前加關鍵字typedef,剩下的不變,本來定義的對象標識符換成類型標識符,對應語義從定義一個對象改爲定義一個類型別名。typedef看起來複雜根本緣由是對象定義的語法比較複雜,例如分隔符*和[]的用法。 針對經典的const來個例子
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;//error
複製代碼
那麼爲何p2++
爲何會報錯呢? 咱們來分析一下,const char *p1 = string;
是聲明瞭一個const char
的指針,不可變的是char
,至關於(const char)*p=string
,因此p1++
不會報錯。p2++
報錯根本緣由是p2
是不可變的,const pStr p2 = string
至關於const (char *) p2 = string
,const
修飾的是char *
,因此p2
不可改變。p1
是數組,p2
是固定的值。
typedef
如何使用呢?咱們具體看幾個案例分析一下。
案例1:
int (*b) (void (*)());
複製代碼
簡化一下是:
typedef void (*func)()
typedef int (*ifunc)(func)
複製代碼
最終簡化成ifunc b;
。能夠理解成(void (*)())
是一個func
,而後替換func
成了最終的形參是func
,返回值是int
。
案例2分析:
int (*func)(int *p);
複製代碼
首先找到func
,func
左邊是*
,說明func
是個指針,而後跳出這個圓括號,先看右邊,又遇到圓括號,這說明(*func)
是一個函數,因此 func是一個指向這類函數的指針,即函數指針,這類函數具備int*
類型的形參,返回值類型是int
。
綜合案例:
SEL didload = @selector(viewDidLoad);
Method md = class_getInstanceMethod(aclass, didload);
IMP load = method_getImplementation(md);
void(*loadFunc)(id,SEL) = (void *)load;
複製代碼
這是將SEL獲取了Method以後將IMP轉化成void(*loadFunc)(id,SEL)
,調用的時候能夠直接調用。
//執行ViewDidLoad IMP
loadFunc(aclass,NULL);
複製代碼
Method能夠這樣使用,block一樣也能夠這樣使用:
void (^block)(id _self) = ^(id _self){
//code here
};
void(*func)(id,SEL) = (void*)imp_implementationWithBlock(block);
class_replaceMethod(aclass, didload, (IMP)func, method_getTypeEncoding(md));
複製代碼
這能夠直接使用runtime/message.h
函數imp_implementationWithBlock
將block
轉化成IMP
,使用class_replaceMethod
替換某個函數的IMP
。那麼再調用該函數的時候,則是調用的block
的IMP
。獲取method
和block
參數後續再分析。
資料參考: