C語言的那些小秘密之函數(shù)指針
函數(shù)是由執(zhí)行語句組成的指令序列或者代碼,這些代碼的有序集合根據(jù)其大小被分配到一定的內(nèi)存空間中,這一片內(nèi)存空間的起始地址就成為函數(shù)的地址,不同的函數(shù)有不同的函數(shù)地址,編譯器通過函數(shù)名來索引函數(shù)的入口地址,為了方便操作類型屬性相同的函數(shù),c/c++引入了函數(shù)指針,函數(shù)指針就是指向代碼入口地址的指針,是指向函數(shù)的指針變量。 因而“函數(shù)指針”本身首先應(yīng)該是指針變量,只不過該指針變量指向函數(shù)。這正如用指針變量可指向整形變量、字符型、數(shù)組一樣,這里是指向函數(shù)。C在編譯時,每一個函數(shù)都有一個入口地址,該入口地址就是函數(shù)指針?biāo)赶虻牡刂?。有了指向函?shù)的指針變量后,可用該指針變量調(diào)用函數(shù),就如同用指針變量可引用其他類型變量一樣,在這些概念上是一致的。函數(shù)指針有兩個用途:調(diào)用函數(shù)和做函數(shù)的參數(shù)。
函數(shù)指針的聲明方法為:
數(shù)據(jù)類型標(biāo)志符 (指針變量名) (形參列表);
“函數(shù)類型”說明函數(shù)的返回類型,由于“”的優(yōu)先級高于“*”,所以指針變量名外的括號必不可少,后面的“形參列表”表示指針變量指向的函數(shù)所帶的參數(shù)列表。例如:
- int function(int x,int y); /* 聲明一個函數(shù) */
- int (*f) (int x,int y); /* 聲明一個函數(shù)指針 */
f=function; /* 將function函數(shù)的首地址賦給指針f */
賦值時函數(shù)function不帶括號,也不帶參數(shù),由于function代表函數(shù)的首地址,因此經(jīng)過賦值以后,指針f就指向函數(shù)function(int x,int y);的代碼的首地址。
下面的程序說明了函數(shù)指針調(diào)用函數(shù)的方法:
例一、
- #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));
- }
注意:以上代碼的紅色部分我們將會在接下來的代碼分析部分進(jìn)行講解,讀者也可以思考下如果運行注釋部分,結(jié)果是否還是正確的呢?
f是指向函數(shù)的指針變量,所以可把函數(shù)max賦給f作為f的值,即把max的入口地址賦給f,以后就可以用f來調(diào)用該函數(shù),實際上f和max都指向同一個入口地址,不同就是f是一個指針變量,不像函數(shù)名稱那樣是死的,它可以指向任何函數(shù),就看你想怎么做了。在程序中把哪個函數(shù)的地址賦給它,它就指向哪個函數(shù)。而后用指針變量調(diào)用它,因此可以先后指向不同的函數(shù)。不過注意,指向函數(shù)的指針變量沒有++和--運算,用時要小心。
函數(shù)括號中的形參可有可無,視情況而定,不過,在某些編譯器中這是不能通過的。這個例子的補(bǔ)充如下。
1.定義函數(shù)指針類型:
- typedef int (*fun_ptr)(int,int);
2.申明變量,賦值:
- fun_ptr max_func=max;
也就是說,賦給函數(shù)指針的函數(shù)應(yīng)該和函數(shù)指針?biāo)傅暮瘮?shù)原型是一致的。
例二、
- #include
- void FileFunc
- {
- printf("FileFunc\n");
- }
- void EditFunc
- {
- printf("EditFunc\n");
- }
- void main
- {
- typedef void (*funcp);
- funcp pfun= FileFunc;
- pfun;
- pfun = EditFunc;
- pfun;
- }
看了上面兩段代碼,應(yīng)該都知道如何用函數(shù)指針來調(diào)用函數(shù)了,但是我們剛剛在上面的描述中留下過一個問題,就是運行注釋部分f=&max;結(jié)果是否還是正確的呢?下面我就給出上面兩個運行結(jié)果的對別,然后來分析下原因。
把注釋部分加進(jìn)去的運行結(jié)果為:
對比以上的運行結(jié)果可以看出,f=&max語句被執(zhí)行時的結(jié)果和沒有被執(zhí)行時的結(jié)果是一樣的。為什么會出現(xiàn)這樣的結(jié)果呢?答案是這是編譯器處理的,max本身就是個地址,它沒有放到任何變量里,自然沒有取它的地址一說。所以我們可以看看在調(diào)試的過程中&max的值和max的值是一樣的。調(diào)試代碼如下:
- 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
- 2 int max ( int x, int y){ return x>y?x:y;}
- (gdb) print max
- $3 = {int (int, int)} 0x80483c4
- (gdb) print &max
- $4 = (int (*)(int, int)) 0x80483c4
- (gdb) print *max
- $5 = {int (int, int)} 0x80483c4
- (gdb) s
- max (x=2, y=6) at hanshu.c:2
- (gdb) s
- main at hanshu.c:8
- 8 f=min;
- (gdb) print min
- $6 = {int (int, int)} 0x80483d3
- (gdb) print &min
- $7 = (int (*)(int, int)) 0x80483d3
- (gdb) print *min
- $8 = {int (int, int)} 0x80483d3
- (gdb) s
- 9 printf (" %d,%d\t" , min (2,6), (f)(5,4));
- (gdb) print f
- $9 = (int (*)(int, int)) 0x80483d3
- (gdb) print &f
- $10 = (int (**)(int, int)) 0xbffff6ac
- (gdb) print *f
- $11 = {int (int, int)} 0x80483d3
- (gdb) s
- min (x=5, y=4) at hanshu.c:3
- 3 int min ( int x, int y){ return x
- (gdb) s
- min (x=2, y=6) at hanshu.c:3
- (gdb) print min
- $12 = {int (int, int)} 0x80483d3
- (gdb) s
- main at hanshu.c:10
- 10 }
在調(diào)試的過程中我print了很多的信息,細(xì)心的讀者肯定能獲得更多的收獲,尤其是對變量f的print,讀者可以自己閱讀,學(xué)到更多的東西。我給出的只是一個參考的調(diào)試方式,希望讀者能夠舉一反三,自己對代碼進(jìn)行實際的調(diào)試,加深理解。
上面說的都是用指針來實現(xiàn)函數(shù)的調(diào)用,接下來我們看一個用函數(shù)指針作為參數(shù)的用法。
- #include
- 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'<
- }
- void main
- {
- int i=10;
- fun2(i,fun1);
- }
運行結(jié)果如下:
看了上面的描述,我想都對函數(shù)指針的概念有了大致的了解,另外一個希望大家不要混淆的概念就是指針函數(shù),,這兩個概念都是簡稱,指針函數(shù)是指帶指針的函數(shù),即本質(zhì)是一個函數(shù)。我們知道函數(shù)都又有返回類型(如果不返回值,則為無值型,即為void),只不過指針函數(shù)返回類型是某一類型的指針。
其定義格式如下所示:
返回類型標(biāo)識符 *返回名稱(形式參數(shù)表)
{ 函數(shù)體}
返回類型可以是任何基本類型和復(fù)合類型。返回指針的函數(shù)的用途十分廣泛。事實上,每一個函數(shù),即使它不帶有返回某種類型的指針,它本身都有一個入口地址,該地址相當(dāng)于一個指針。比如函數(shù)返回一個整型值,實際上也相當(dāng)于返回一個指針變量的值,不過這時的變量是函數(shù)本身而已,而整個函數(shù)相當(dāng)于一個“變量”,關(guān)于函數(shù)的返回值問題我將在下一章來講解,本章到此為止。希望以上內(nèi)容對你有所幫助!
C語言博大精深,由于本人水平有限,博客中的不妥或錯誤之處在所難免,殷切希望讀者批評指正。同時也歡迎讀者共同探討相關(guān)的內(nèi)容,如果樂意交流的話請留下你寶貴的意見。