C語言進階:指針的進階(4)

函數指針

函數指針的定義

整型指針存放整型的地址;數組指針存放數組的地址;那麼類比可得,函數指針存放函數的地址。

顯然,函數指針指向函數,存放函數的地址。搞懂函數指針,先瞭解函數的地址。

在這裡插入圖片描述

&函數名或函數名代表函數地址,與&數組名和數組名略有不同,&函數名和函數名完全一致。

函數的地址必然要放到函數指針裡,函數指針的類型該如何寫呢?(以Add函數為例)

//整型指針
int* pa = &a;
//字符指針 
char* pc = &ch;
//數組指針
int(*pa)[10] = &arr;
//函數指針 - 存放函數地址
int(*pf)(int, int) = &Add;

函數指針的類型

int Add(int x, int y);
//1.
int(*pf)(int, int) = &Add;
//2.
int *pf(int, int) = &Add;

倘若,去掉括號int* pf(int, int),pf就變成函數名,返回類型是int*。所以指針必須帶括號。

前文已交代,指針,去掉指針名和*就是指針所指向的變量類型。

  • 整型指針,去掉*和指針名,即為整型變量類型int。字符指針,為字符類型char。數組指針,去掉後為數組類型int[10]。
  • 函數指針,去掉*和指針名,即為函數的類型int(int,int)。

總結

  • 去掉指針名pf,即為指針類型int(*)(int, int)
  • 去掉指針名pf和*,即為指針所指向的函數類型為int(int, int)

函數指針的使用

計算機硬件程序經常通過調用地址的方式來調用函數,因此需要使用函數指針調用函數。

int Add(int x, int y)
{
	return x + y;
}
int main()
{
    //1.
    int(*pf)(int, int) = &Add;//函數指針指向Add函數
    //2.
    int(*pf)(int, int) = Add;
	
    //1.
	int ret = (*pf)(2, 3);
	//2.
    int ret = pf(2, 3);
	
    printf("%d\n", ret);
	return 0;
}

前面已經交代,&函數名和函數名都是函數的地址,完全等價。所以兩種初始化函數指針的方式都可以。

既然函數名Add可以直接賦值給函數指針pf,說明二者等價。函數指針名pf不解引用也可以使用,*在此處形同虛設,甚至於不寫或寫多個都並無大礙,僅為理解。

既然函數名也是函數地址,所以對其解引用也是可以的。我們甚至可以這樣寫,但僅限娛樂,沒有必要。

Add(2, 3);//1
(*Add)(2, 3);//2
(*&Add)(2, 3);//3

Example

解釋下列代碼

//1.
(*(void(*)())0)();
//2.
void (*signal(int, void(*)(int)))(int);

在這裡插入圖片描述

1.void(*)()是函數指針類型,放在( )0中,也就是把0強制轉換成地址,該地址處存放一個函數其類型為void(*)(void)

2.這樣(void(*)())0就變成瞭指針,指向該地址的函數,且對其解引用訪問此函數。

3.(*(void(*)())0)也相當於(*pf),通過函數指針解引用代替函數名,函數名後面帶上();,相當於(*pf)();也就是一次不傳參的函數調用。

在這裡插入圖片描述

1.signal先和()結合,說明signal為函數名,其後(int, void(*)(int)),為其參數列表。

2.去掉函數名稱和參數列表,剩下的void(*)(int)就是返回類型,所以是一次函數聲明。

void (* signal(int, void(*)(int)) ) (int);

typedef void(* pf_t)(int);//typedef簡化代碼
pf_t signal(int, pf_t);

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: