天天看點

C語言函數指針

顧名思義,函數指針就是函數的指針。它是一個指針,指向一個函數。看例子:

a)

char * (*fun1)(char * p1,char * p2);

b)

char * *fun2(char * p1,char * p2);

c)

char * fun3(char * p1,char * p2);

看看上面三個表達式分别是什麼意思?

c):這很容易,fun3 是函數名,p1,p2 是參數,其類型為char *型,函數的傳回值為char *類型。

b):也很簡單,與c)表達式相比,唯一不同的就是函數的傳回值類型為char**,是個二級指針。

a):fun1 是函數名嗎?回憶一下前面講解數組指針時的情形。我們說數組指針這麼定義或許更清晰:

   int (*)[10] p;

再看看a)表達式與這裡何其相似!明白了吧。這裡fun1 不是什麼函數名,而是一個指針變量,它指向一個函數。這個函數有兩個指針類型的參數,函數的傳回值也是一個指針。同樣,我們把這個表達式改寫一下:char * (*)(char * p1,char * p2) fun1; 這樣子是不是好看一些呢?隻可惜編譯器不這麼想。^_^。

1、函數指針使用的例子

上面我們定義了一個函數指針,但如何來使用它呢?先看如下例子:

#include <stdio.h>

#include <string.h>

char * fun(char * p1,char * p2)

{

   int i = 0;

   i = strcmp(p1,p2);

   if (0 == i)

   {

      return p1;

   }

   else

      return p2;

}

intmain()

   char * (*pf)(char * p1,char * p2);

   pf = &fun;

   (*pf) ("aa","bb");

   return 0;

我們使用指針的時候,需要通過鑰匙(“*”)來取其指向的記憶體裡面的值,函數指針使用也如此。通過用(*pf)取出存在這個位址上的函數,然後調用它。這裡需要注意到是,在visual c++6.0 裡,給函數指針指派時,可以用&fun 或直接用函數名fun。這是因為函數名被編譯之後其實就是一個位址,是以這裡兩種用法沒有本質的差别。這個例子很簡單,就不再詳細讨論了。

2、*(int*)&p ----這是什麼?

也許上面的例子過于簡單,我們看看下面的例子:

void function()

   printf("call function!\n");

   void (*p)();

   *(int*)&p=(int)function;

   (*p) ();

這是在幹什麼?*(int*)&p=(int)function;表示什麼意思?别急,先看這行代碼:

這行代碼定義了一個指針變量p,p 指向一個函數,這個函數的參數和傳回值都是void。

&p 是求指針變量p 本身的位址,這是一個32 位的二進制常數(32 位系統)。

(int*)&p 表示将位址強制轉換成指向int 類型資料的指針。

(int)function 表示将函數的入口位址強制轉換成int 類型的資料。

分析到這裡,相信你已經明白*(int*)&p=(int)function;表示将函數的入口位址指派給指針變量p。那麼(*p) ();就是表示對函數的調用。

講解到這裡,相信你已經明白了。其實函數指針與普通指針沒什麼差别,隻是指向的内容不同而已。

使用函數指針的好處在于,可以将實作同一功能的多個子產品統一起來辨別,這樣一來更容易後期的維護,系統結構更加清晰。或者歸納為:便于分層設計、利于系統抽象、降低耦合度以及使接口與實作分開。

3、(*(void(*) ())0)()------這是什麼?

是不是感覺上面的例子太簡單,不夠刺激?好,那就來點刺激的,看下面這個例子:

   (*(void(*) ())0)();

這是《c traps and pitfalls》這本經典的書中的一個例子。沒有發狂吧?下面我們就來分析分析:

第一步:void(*) (),可以明白這是一個函數指針類型。這個函數沒有參數,沒有傳回值。

第二步:(void(*) ())0,這是将0 強制轉換為函數指針類型,0 是一個位址,也就是說一個函數存在首位址為0 的一段區域内。

第三步:(*(void(*) ())0),這是取0 位址開始的一段記憶體裡面的内容,其内容就是儲存在首位址為0 的一段區域内的函數。

第四步:(*(void(*) ())0)(),這是函數調用。

好像還是很簡單是吧,上面的例子再改寫改寫:

   (*(char**(*) (char **,char **))0) ( char **,char **);

如果沒有上面的分析,肯怕不容易把這個表達式看明白吧。不過現在應該是很簡單的一件事了。讀者以為呢?

4、函數指針數組

現在我們清楚表達式“char * (*pf)(char * p)”定義的是一個函數指針pf。既然pf 是一個指針,那就可以儲存在一個數組裡。把上式修改一下:

   char * (*pf[3])(char * p);

這是定義一個函數指針數組。它是一個數組,數組名為pf,數組記憶體儲了3 個指向函數的指針。這些指針指向一些傳回值類型為指向字元的指針、參數為一個指向字元的指針的函數。這念起來似乎有點拗口。不過不要緊,關鍵是你明白這是一個指針數組,是數組。

函數指針數組怎麼使用呢?這裡也給出一個非常簡單的例子,隻要真正掌握了使用方法,再複雜的問題都可以應對。如下:

char * fun1(char * p)

   printf("%s\n",p);

   return p;

char * fun2(char * p)

char * fun3(char * p)

   pf[0] = fun1; // 可以直接用函數名

   pf[1] = &fun2; // 可以用函數名加上取位址符

   pf[2] = &fun3;

   pf[0]("fun1");

   pf[0]("fun2");

   pf[0]("fun3");

5、函數指針數組的指針

看着這個标題沒發狂吧?函數指針就夠一般初學者折騰了,函數指針數組就更加麻煩,現在的函數指針數組指針就更難了解了。

其實,沒這麼複雜。前面詳細讨論過數組指針的問題,這裡的函數指針數組指針不就是一個指針嘛。隻不過這個指針指向一個數組,這個數組裡面存的都是指向函數的指針。僅此而已。

下面就定義一個簡單的函數指針數組指針:

   char * (*(*pf)[3])(char * p);

注意,這裡的pf 和上一節的pf 就完全是兩碼事了。上一節的pf 并非指針,而是一個數組名;這裡的pf 确實是實實在在的指針。這個指針指向一個包含了3 個元素的數組;這個數字裡面存的是指向函數的指針;這些指針指向一些傳回值類型為指向字元的指針、參數為一個指向字元的指針的函數。這比上一節的函數指針數組更拗口。其實你不用管這麼多,明白這是一個指針就ok 了。其用法與前面講的數組指針沒有差别。下面列一個簡單的例子:

   char * (*a[3])(char * p);

   pf = &a;

   a[0] = fun1;

   a[1] = &fun2;

   a[2] = &fun3;

   pf[0][0]("fun1");

   pf[0][1]("fun2");

   pf[0][2]("fun3");

繼續閱讀