目錄
一、函數指針變量的創建
1、什么是函數指針變量?
2、函數是否有地址?
3、創建函數指針變量
4、函數指針類型解析
二、函數指針變量的使用
三、兩段有趣的代碼
1、解釋?(*(void (*)())0)();
2、解釋?void (*signal(int, void(*)(int)))(int);
四、typedef關鍵字
1、基本用法
2、對于數組指針和函數指針的重命名
3、使用typedef簡化代碼void (*signal(int, void(*)(int)))(int);
五、函數指針數組
一、函數指針變量的創建
1、什么是函數指針變量?
????????類比整型指針和數組指針的概念,我們可以得出:函數指針變量是用來存放函數地址的變量,通過這個地址我們可以調用相應的函數。
2、函數是否有地址?
通過以下測試代碼可以驗證:
#include <stdio.h>void test() {printf("hehe\n");
}int main() {printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}
輸出結果:
????????結果表明函數確實有地址,且函數名就是函數的地址,也可以通過&函數名
的方式獲取函數地址。(重點!!!)
3、創建函數指針變量
函數指針變量的聲明語法與數組指針類似:
void test() {printf("hehe\n");
}// 兩種等效的函數指針聲明方式
void (*pf1)() = &test;
void (*pf2)() = test;int Add(int x, int y) {return x + y;
}// 函數指針聲明,參數名可省略
int (*pf3)(int, int) = Add;
int (*pf4)(int x, int y) = &Add;
4、函數指針類型解析
以int (*pf3)(int x, int y)
為例:int (*) (int x, int y) 為pf3函數指針變量的類型
二、函數指針變量的使用
通過函數指針調用函數的示例:
#include <stdio.h>int Add(int x, int y) {return x + y;
}int main() {int(*pf)(int, int) = Add;// 兩種等效的調用方式printf("%d\n", (*pf)(2, 3)); // 顯式解引用printf("%d\n", pf(3, 5)); // 隱式調用return 0;
}
輸出結果:
三、兩段有趣的代碼
以下兩段有趣的代碼出自《C陷阱和缺陷》:
1、解釋?(*(void (*)())0)();
這段代碼的功能是:調用位于內存地址0處的函數。
讓我們逐步解析:
-
void (*)():
這是一個函數指針類型,表示"指向一個沒有參數且返回void的函數的指針"。 -
(void (*)())0:
將整數值0強制轉換為上述函數指針類型。這表示"把地址0當作一個函數的地址"。 -
*(void (*)())0:
解引用這個函數指針,得到位于地址0處的函數。 -
(*(void (*)())0)():
最后調用這個函數(通過函數指針調用)。
實際意義:這段代碼嘗試調用內存地址0處的函數。在嵌入式系統中,這可能是調用復位向量或啟動代碼的方式。但在大多數現代操作系統中,這會引發段錯誤(segmentation fault),因為地址0通常是被保護的區域。
2、解釋?void (*signal(int, void(*)(int)))(int);
這是一個函數聲明,聲明了名為signal
的函數。讓我們分解它:
-
最內層:
void(*)(int):
這是一個函數指針類型,表示"指向一個接受int參數且返回void的函數的指針"。 -
signal(int, void(*)(int)):
signal
是一個函數,它接受兩個參數:一個int、一個上述類型的函數指針 -
整個聲明:
void (*signal(int, void(*)(int)))(int):
表示signal
函數返回一個函數指針,這個指針指向"接受int參數且返回void的函數"。
更易讀的寫法(使用typedef,下面會講解):
typedef void (*sighandler_t)(int); // 定義函數指針類型sighandler_t signal(int signum, sighandler_t handler);
實際意義:這是Unix/Linux系統中用于設置信號處理器的標準函數聲明。它:
-
接受一個信號編號(int)和一個處理該信號的函數指針
-
返回之前為該信號設置的處理函數指針
四、typedef關鍵字
typedef用于類型重命名,可以簡化復雜類型的聲明。
1、基本用法
typedef unsigned int uint; // 將unsigned int重命名為uint
typedef int* ptr_t; // 將int*重命名為ptr_t
2、對于數組指針和函數指針的重命名
新的類型名必須在*的右邊:
typedef int(*parr_t)[5]; // 將數組指針類型int(*)[5]重命名為parr_t
typedef void(*pfun_t)(int); // 將函數指針類型void(*)(int)重命名為pfun_t
3、使用typedef簡化代碼void (*signal(int, void(*)(int)))(int);
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
五、函數指針數組
數組是存放相同類型數據的存儲空間。我們已經學過指針數組:
int *arr[10]; // 數組的每個元素是int*
如果要存儲函數地址,就需要使用函數指針數組。函數指針數組的定義方式:
int (*parr1[3])(); // 正確:包含3個函數指針的數組,每個指針指向返回int的無參函數
下面是錯誤用法:
int *parr2[3](); // 錯誤:錯誤語法
int (*)() parr3[3]; // 錯誤:錯誤語法
解析parr1:
-
parr1
先與[]
結合,說明它是一個數組 -
數組的內容是
int (*)()
類型的函數指針
函數指針數組常用于實現狀態機或回調函數機制,是C語言中一種強大的編程技術。