天天看點

C語言的那些小秘密之函數指針

函數是由執行語句組成的指令序列或者代碼,這些代碼的有序集合根據其大小被配置設定到一定的記憶體空間中,這一片記憶體空間的起始位址就成為函數的位址,不同的函數有不同的函數位址,編譯器通過函數名來索引函數的入口位址,為了友善操作類型屬性相同的函數,c/c++引入了函數指針,函數指針就是指向代碼入口位址的指針,是指向函數的指針變量。

因而“函數指針”本身首先應該是指針變量,隻不過該指針變量指向函數。這正如用指針變量可指向整形變量、字元型、數組一樣,這裡是指向函數。c在編譯時,每一個函數都有一個入口位址,該入口位址就是函數指針所指向的位址。有了指向函數的指針變量後,可用該指針變量調用函數,就如同用指針變量可引用其他類型變量一樣,在這些概念上是一緻的。函數指針有兩個用途:調用函數和做函數的參數。

C語言的那些小秘密之函數指針

函數指針的聲明方法為:

資料類型标志符 (指針變量名) (形參清單);

“函數類型”說明函數的傳回類型,由于“”的優先級高于“*”,是以指針變量名外的括号必不可少,後面的“形參清單”表示指針變量指向的函數所帶的參數清單。例如:

int function(int x,int y); /* 聲明一個函數 */ 

int (*f) (int x,int y); /* 聲明一個函數指針 */ 

f=function; /* 将function函數的首位址賦給指針f */

指派時函數function不帶括号,也不帶參數,由于function代表函數的首位址,是以經過指派以後,指針f就指向函數function(int x,int y);的代碼的首位址。

下面的程式說明了函數指針調用函數的方法:

例一、

#include 

int max ( int x, int y){ return x>y?x:y;} 

int min ( int x, int y){ return x 

void main 

{ int ( *f ) ( int x, int y)=max; 

//f=&max; 

printf ( "%d,%d\t", max (2,6), (f)(5,4)); 

f=min; 

printf (" %d,%d\t" , min (2,6), (f)(5,4)); 

注意:以上代碼的紅色部分我們将會在接下來的代碼分析部分進行講解,讀者也可以思考下如果運作注釋部分,結果是否還是正确的呢?

f是指向函數的指針變量,是以可把函數max賦給f作為f的值,即把max的入口位址賦給f,以後就可以用f來調用該函數,實際上f和max都指向同一個入口位址,不同就是f是一個指針變量,不像函數名稱那樣是死的,它可以指向任何函數,就看你想怎麼做了。在程式中把哪個函數的位址賦給它,它就指向哪個函數。而後用指針變量調用它,是以可以先後指向不同的函數。不過注意,指向函數的指針變量沒有++和--運算,用時要小心。

函數括号中的形參可有可無,視情況而定,不過,在某些編譯器中這是不能通過的。這個例子的補充如下。

1.定義函數指針類型:

typedef int (*fun_ptr)(int,int); 

2.申明變量,指派:

fun_ptr max_func=max; 

也就是說,賦給函數指針的函數應該和函數指針所指的函數原型是一緻的。

例二、

void filefunc 

printf("filefunc\n"); 

void editfunc 

printf("editfunc\n"); 

typedef void (*funcp); 

funcp pfun= filefunc; 

pfun; 

pfun = editfunc; 

看了上面兩段代碼,應該都知道如何用函數指針來調用函數了,但是我們剛剛在上面的描述中留下過一個問題,就是運作注釋部分f=&max;結果是否還是正确的呢?下面我就給出上面兩個運作結果的對别,然後來分析下原因。

C語言的那些小秘密之函數指針

把注釋部分加進去的運作結果為:

C語言的那些小秘密之函數指針

對比以上的運作結果可以看出,f=&max語句被執行時的結果和沒有被執行時的結果是一樣的。為什麼會出現這樣的結果呢?答案是這是編譯器處理的,max本身就是個位址,它沒有放到任何變量裡,自然沒有取它的位址一說。是以我們可以看看在調試的過程中&max的值和max的值是一樣的。調試代碼如下:

root@ubuntu:/home/shiyan# gdb ss 

gnu gdb (ubuntu/linaro 7.2-1ubuntu11) 7.2 

copyright (c) 2010 free software foundation, inc. 

license gplv3+: gnu gpl version 3 or later 

this is free software: you are free to change and redistribute it. 

there is no warranty, to the extent permitted by law. type "show copying" 

and "show warranty" for details. 

this gdb was configured as "i686-linux-gnu". 

for bug reporting instructions, please see: 

... 

reading symbols from /home/shiyan/ss...done. 

(gdb) list 

1 #include 

2 int max ( int x, int y){ return x>y?x:y;} 

3 int min ( int x, int y){ return x 

4 void main 

5 { int ( *f ) ( int x, int y)=max; 

6 //f=&max; 

7 printf ( "%d,%d\t", max (2,6), (f)(5,4)); 

8 f=min; 

9 printf (" %d,%d\t" , min (2,6), (f)(5,4)); 

10 } 

(gdb) b 4 

breakpoint 1 at 0x80483ec: file hanshu.c, line 4. 

(gdb) r 

starting program: /home/shiyan/ss 

breakpoint 1, main at hanshu.c:5 

(gdb) print max 

$1 = {int (int, int)} 0x80483c4 

(gdb) print f 

$2 = (int (*)(int, int)) 0xbffff6c8 

(gdb) s 

(gdb) 

max (x=5, y=4) at hanshu.c:2 

$3 = {int (int, int)} 0x80483c4 

(gdb) print &max 

$4 = (int (*)(int, int)) 0x80483c4 

(gdb) print *max 

$5 = {int (int, int)} 0x80483c4 

max (x=2, y=6) at hanshu.c:2 

main at hanshu.c:8 

(gdb) print min 

$6 = {int (int, int)} 0x80483d3 

(gdb) print &min 

$7 = (int (*)(int, int)) 0x80483d3 

(gdb) print *min 

$8 = {int (int, int)} 0x80483d3 

$9 = (int (*)(int, int)) 0x80483d3 

(gdb) print &f 

$10 = (int (**)(int, int)) 0xbffff6ac 

(gdb) print *f 

$11 = {int (int, int)} 0x80483d3 

min (x=5, y=4) at hanshu.c:3 

min (x=2, y=6) at hanshu.c:3 

$12 = {int (int, int)} 0x80483d3 

main at hanshu.c:10 

在調試的過程中我print了很多的資訊,細心的讀者肯定能獲得更多的收獲,尤其是對變量f的print,讀者可以自己閱讀,學到更多的東西。我給出的隻是一個參考的調試方式,希望讀者能夠舉一反三,自己對代碼進行實際的調試,加深了解。

上面說的都是用指針來實作函數的調用,接下來我們看一個用函數指針作為參數的用法。

using namespace std; 

typedef int (*print)(int ); 

int fun1(int i) 

return (int)i; 

void fun2(int j,print prt) 

for(int k=0;k 

cout<<'\t'< 

int i=10; 

fun2(i,fun1); 

運作結果如下:

C語言的那些小秘密之函數指針

看了上面的描述,我想都對函數指針的概念有了大緻的了解,另外一個希望大家不要混淆的概念就是指針函數,,這兩個概念都是簡稱,指針函數是指帶指針的函數,即本質是一個函數。我們知道函數都又有傳回類型(如果不傳回值,則為無值型,即為void),隻不過指針函數傳回類型是某一類型的指針。

其定義格式如下所示:

傳回類型辨別符 *傳回名稱(形式參數表)

{ 函數體}

傳回類型可以是任何基本類型和複合類型。傳回指針的函數的用途十分廣泛。事實上,每一個函數,即使它不帶有傳回某種類型的指針,它本身都有一個入口位址,該位址相當于一個指針。比如函數傳回一個整型值,實際上也相當于傳回一個指針變量的值,不過這時的變量是函數本身而已,而整個函數相當于一個“變量”,關于函數的傳回值問題我将在下一章來講解,本章到此為止。希望以上内容對你有所幫助!

c語言博大精深,由于本人水準有限,部落格中的不妥或錯誤之處在所難免,殷切希望讀者批評指正。同時也歡迎讀者共同探讨相關的内容,如果樂意交流的話請留下你寶貴的意見。

來源:51cto

繼續閱讀