c語言函數指針的理解與使用(學習)


1.函數指針的定義


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

1
2
3
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是函數名嗎?回憶一下前面講解數組指針時的情形。我們說數組指針這么定義或許更清晰:

1
int  (*)[ 10 ] p;

再看看A)表達式與這里何其相似!明白了吧。這里fun1不是什么函數名,而是一個指針變量,它指向一個函數。這個函數有兩個指針類型的參數,函數的返回值也是一個指針。同樣,我們把這個表達式改寫一下:

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

這樣子是不是好看一些呢?只可惜編譯器不這么想。^_^。

 

2.函數指針使用的例子


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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;
  }
}
 
int  main()
{
   char  * (*pf)( char  * p1, char  * p2);
  pf = &fun;
  (*pf) ( "aa" , "bb" );
   return  0 ;
}

  我們使用指針的時候,需要通過鑰匙(“*”)來取其指向的內存里面的值,函數指針使用也如此。通過用(*pf)取出存在這個地址上的函數,然后調用它。

  這里需要注意到是,在Visual C++6.0里,給函數指針賦值時,可以用&fun或直接用函數名fun。這是因為函數名被編譯之后其實就是一個地址,所以這里兩種用法沒有本質的差別。這個例子很簡單,就不再詳細討論了。

 

3.*(int*)&p ----這是什么?


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

1
2
3
4
5
6
7
8
9
10
11
void  Function()
{
  printf( "Call Function!\n" );
}<br>
int  main()
{
   void  (*p)();
  *( int *)&p=( int )Function;
  (*p)();
   return  0 ;
} 


這是在干什么?*(int*)&p=(int)Function;表示什么意思?
別急,先看這行代碼:

1
void  (*p)();

這行代碼定義了一個指針變量p,p指向一個函數,這個函數的參數和返回值都是void。
&p是求指針變量p本身的地址,這是一個32位的二進制常數(32位系統)。
(int*)&p表示將地址強制轉換成指向int類型數據的指針。
(int)Function表示將函數的入口地址強制轉換成int類型的數據。
分析到這里,相信你已經明白*(int*)&p=(int)Function;表示將函數的入口地址賦值給指針變量p。


那么(*p) ();就是表示對函數的調用。


講解到這里,相信你已經明白了。其實函數指針與普通指針沒什么差別,只是指向的內容不同而已。
使用函數指針的好處在於,可以將實現同一功能的多個模塊統一起來標識,這樣一來更容易后期的維護,系統結構更加清晰。或者歸納為:便於分層設計、利於系統抽象、降低耦合度以及使接口與實現分開。

 

 

4.(*(void(*) ())0)()------這是什么?


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

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

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

1
2
3
4
第一步: void (*) (),可以明白這是一個函數指針類型。這個函數沒有參數,沒有返回值。
第二步:( void (*) ()) 0 ,這是將 0 強制轉換為函數指針類型, 0 是一個地址,也就是說一個函數存在首地址為 0 的一段區域內。
第三步:(*( void (*) ()) 0 ),這是取 0 地址開始的一段內存里面的內容,其內容就是保存在首地址為 0 的一段區域內的函數。
第四步:(*( void (*) ()) 0 )(),這是函數調用。


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

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


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

 

5.函數指針數組

 

  現在我們清楚表達式

1
char  * (*pf)( char  * p);

定義的是一個函數指針pf。既然pf是一個指針,那就可以儲存在一個數組里。把上式修改一下:

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

這是定義一個函數指針數組。

 

  它是一個數組,數組名為pf,數組內存儲了3個指向函數的指針。這些指針指向一些返回值類型為指向字符的指針、參數為一個指向字符的指針的函數。

  這念起來似乎有點拗口。不過不要緊,關鍵是你明白這是一個指針數組,是數組。函數指針數組怎么使用呢?這里也給出一個非常簡單的例子,只要真正掌握了使用方法,再復雜的問題都可以應對。

 

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <string.h>
<br> char  * fun1( char  * p)
{
  printf( "%s\n" ,p);
   return  p;
}
 
char  * fun2( char  * p)
{
  printf( "%s\n" ,p);
   return  p;
}
char  * fun3( char  * p)
{
  printf( "%s\n" ,p);
   return  p;
}
<br> int  main()
{
   char  * (*pf[ 3 ])( char  * p);
  pf[ 0 ] = fun1;  //可以直接用函數名
  pf[ 1 ] = &fun2;  //可以用函數名加上取地址符
  pf[ 2 ] = &fun3;<br>
  pf[ 0 ]( "fun1" );
  pf[ 0 ]( "fun2" );
  pf[ 0 ]( "fun3" );
   return  0 ;
} 

 

6.函數指針數組的指針


  看着這個標題沒發狂吧?函數指針就夠一般初學者折騰了,函數指針數組就更加麻煩,現在的函數指針數組指針就更難理解了。
其實,沒這么復雜。前面詳細討論過數組指針的問題,這里的函數指針數組指針不就是一個指針嘛。只不過這個指針指向一個數組,這個數組里面存的都是指向函數的指針。僅此而已。


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

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


注意,這里的pf和上一節的pf就完全是兩碼事了。上一節的pf並非指針,而是一個數組名;這里的pf確實是實實在在的指針。這個指針指向一個包含了3個元素的數組;這個數字里面存的是指向函數的指針;這些指針指向一些返回值類型為指向字符的指針、參數為一個指向字符的指針的函數。

  這比上一節的函數指針數組更拗口。其實你不用管這么多,明白這是一個指針就ok了。其用法與前面講的數組指針沒有差別。下面列一個簡單的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <string.h>
 
char  * fun1( char  * p)
{
     printf( "%s\n" ,p);
     return  p;
}
 
char  * fun2( char  * p)
{
     printf( "%s\n" ,p);
     return  p;
}
 
char  * fun3( char  * p)
{
     printf( "%s\n" ,p);
     return  p;
}
 
int  main()
{
     char  * (*a[ 3 ])( char  * p);
     char  * (*(*pf)[ 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" );
     return  0 ;
}

  

 

 

 

 

 

 

 

 

 

 

 

參考:陳正沖老師的《c語言深度剖析》。



作者: 木木
博客文章大部分為原創,版權歸作者和博客園共有,歡迎轉載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM