實質:函數指針實質是一個指針,並不是函數,函數有自己的函數體,而指針只是一個變量
指針函數和函數指針的區別:
指針函數: 實質:是一個函數,有自己的函數體
格式:數據類型關鍵字 *xxx(形參)
{
函數體;
}
函數指針: 實質:是一個指針,一個變量,常用來調用函數
格式:數據類型關鍵字 (*指針名)(形參)
功能:指向函數的指針可用於調用它所指向的函數。可以不需要使用解引用操作符,而直接通過指針調用此函數。
示例1:
typedef void FUN (char , int);
void (*pf)(char, int);//聲明一個函數指針pf,指向一個返回值為0帶有(char,int)兩個形參的函數
FUN fun; //實際就是聲明一個void fun(char,int)的函數
pf=fun;
兩種調用方法如下:
(*pf)('c',90); // 顯式調用
pf('c',90); // 隱式調用
示例 2:通過指針調用函數
1. #include <stdio.h>2.3. 2. // 聲明一個函數指針,指向的函數形參帶有一個char類型,一個int類型,返回類型為 void 3. void (*pf)(char, int); 5. // 聲明一個函數,形參為一個 char 類型,一個 int 類型,返回類型為 void 6. void fun(char ,int); 7. int main() 8. { 9. pf=fun; //函數指針 pf 賦值為 fun 函數的地址(函數名代表函數的地址) 10. (*pf)('c',90); //調用 pf 指向的函數 11. pf('a',80); 12. } 13. void fun(char a,int b) 14. { 15. printf("the argument is %c and %d\n",a,b); 16. }
運行結果: the argument is c and 90
the argument is a and 80
返回指向函數的指針
函數可以返回指向函數的指針,但是,正確寫出這種返回類型相當不容易,例如:
int (*ff(int))(int*,int);
要理解函數指針聲明的最佳方法是從聲明的名字開始由里而外理解。要理解該聲明的含義,首先觀察“ff(int)”
是將 ff 聲明為一個函數,它帶領有一個 int 型的形參。該函數返回“int (*)(int*,int)”它是一個指向函數的指針,所指向的函數返回 int 型並帶有兩個分別為 int *型和 int 型的形參。
使用 typedef 可使該定義更簡明易懂:
typedef int (*PF)(int *,int);
PF ff(int);
示例:
#include <iostream> using namespace std; typedef void (*callback)(int x); // 定義一個函數指針類型 void myFunc1(int x) // myFunc1 的聲明要與callback一樣 { cout << "This is myFunc1 " << x << endl; } void myFunc2(int x) { cout << "This is myFunc2 " << x << endl; } void callMyFunc(callback cb, int x) // 把函數指針類型當做調用函數參數類型 { cb(x); } int main() { callMyFunc(myFunc1, 1); callMyFunc(myFunc2, 2); return 0; }
下面舉幾個typedef和函數指針結合使用的例子:
左邊第一個運行出錯 右邊兩個正確
出錯原因:
typedef定義的pf是一種void (*)(int)的函數指針類型,pf定義的是指針(它是一個數據類型關鍵字,類似於int)。
用pf去聲明fun肯定會出錯,pf fun,是定義fun為一個函數指針變量(可以理解為函數名),而不是聲明函數,右邊兩個是正確的。
函數名是可以作為函數指針變量,作為函數的入口地址,但是函數名不僅僅是這種作用。
函數名相當於表示函數的入口地址,我們調用時通過函數名,讓程序跳轉到對應函數處開始執行
還可這樣使用:
void (*signal(int signum,void(* handler)(int)))(int); 等價於下面兩行
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler)
運行結果: fdsfsfsf1
fdsfsfsf3
解剖一下signal函數,理解函數指針和typedef
void (*signal(int signum,void(* handler)(int)))(int); 等價於下面兩行 typedef void (*sighandler_t)(int); sighandler_t signal(int signum,sighandler_t handler)
分析:先分析第一行,看到這一串的函數聲明,不要慌,從內而外一點一點分析:
1. signal(int signum,void(* handler)(int))這是一個函數,函數名為signal
(1) 第一個形參:int signum,是一個整形變量;
(2) 第二個形參:void(*handler)(int),這是一個函數指針,指向:返回值為void型,且帶一個int型的形參的函數。
也就是說,這個形參,是一個指針,或者說是一個地址,可以理解為這個形參是函數名,因為函數名是函數的入口
地址。里面分析完了。
2. signal函數的外面是 void(*)(int),想一想,在定義或者聲明函數的時候,都需要說明函數返回值的類型,這個signal
函數的形參已經明確了,但是返回值還不知道,這個void(*)(int)就是返回值類型,這個類型是一個函數指針,碰巧的是,
這個函數指針指向的函數類型和signal的第二個形參指向的類型相同,都是指向:返回值為空,且帶一個int類型形參的函數。
3. 因此先用typedef定義一種數據類型,這個類型就是void(*)(int),將這種類型定義為,或者說是起名別,叫做sighandler_t
sighandler_t = void(*)(int),所以可以將void(*)(int)類型的函數,在聲明或者定義的時候用sighandler_t。
4. signal函數返回值為void(*)(int)型,用sighandler_t代替,第2個形參是void(*)(int)型,用sighandler_t代替。