iOS 淺析指針、函數、typedef

指針函數和函數指針

顧名思義,指針函數即返回指針的函數。其通常定義形式以下:程序員

類型名 *函數名(函數參數表列);  
複製代碼

其中,後綴運算符括號「()」表示這是一個函數,其前綴運算符星號「*」表示此函數爲指針型函數,其函數值爲指針,即它帶回來的值的類型爲指針,當調用這個函數後,將獲得一個「指向返回值爲…的指針(地址),「類型名」表示函數返回的指針指向的類型」。數組

「(函數參數表列)」中的括號爲函數調用運算符,在調用語句中,即便函數不帶參數,其參數表的一對括號也不能省略。其示例以下: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);
複製代碼

深刻理解 typedef

平時咱們在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);
複製代碼

首先找到funcfunc左邊是*,說明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_implementationWithBlockblock轉化成IMP,使用class_replaceMethod替換某個函數的IMP。那麼再調用該函數的時候,則是調用的blockIMP。獲取methodblock參數後續再分析。

資料參考:

玉令天下博客地址

相關文章
相關標籤/搜索