日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++函数指针详解

發(fā)布時(shí)間:2024/7/23 c/c++ 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++函数指针详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

來源:http://www.cnblogs.com/ggjucheng/archive/2011/12/13/2286391.html

指針的概念

指針是一個(gè)特殊的變量,它里面存儲的數(shù)值被解釋成為內(nèi)存里的一個(gè)地址。要搞清一個(gè)指針需要搞清指針的四方面的內(nèi)容:指針的類型,指針?biāo)赶虻念愋?#xff0c;指針的值或者叫指針?biāo)赶虻膬?nèi)存區(qū),還有指針本身所占據(jù)的內(nèi)存區(qū)。讓我們分別說明。?

先聲明幾個(gè)指針放著做例子: ?

int *ptr; char *ptr; int **ptr; int (*ptr)[3]; int *(*ptr)[4];

指針的類型

從語法的角度看,只要把指針聲明語句里的指針名字去掉,剩下的部分就是這個(gè)指針的類型。這是指針本身所具有的類型。

int *ptr; //指針的類型是int * char *ptr; //指針的類型是char * int **ptr; //指針的類型是 int ** int (*ptr)[3]; //指針的類型是 int(*)[3] int *(*ptr)[4]; //指針的類型是 int *(*)[4] 怎么樣?找出指針的類型的方法是不是很簡單? ?

指針?biāo)赶虻念愋?/span>

當(dāng)你通過指針來訪問指針?biāo)赶虻膬?nèi)存區(qū)時(shí),指針?biāo)赶虻念愋蜎Q定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來看待。
從語法上看,你只須把指針聲明語句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針?biāo)赶虻念愋汀@?#xff1a; ?

int *ptr; //指針?biāo)赶虻念愋褪莍nt char *ptr; //指針?biāo)赶虻牡念愋褪莄har int **ptr; //指針?biāo)赶虻牡念愋褪?int * int (*ptr)[3]; //指針?biāo)赶虻牡念愋褪?int()[3] int *(*ptr)[4]; //指針?biāo)赶虻牡念愋褪?int *()[4] 在指針的算術(shù)運(yùn)算中,指針?biāo)赶虻念愋陀泻艽蟮淖饔谩??

指針的類型(即指針本身的類型)指針?biāo)赶虻念愋?/span>是兩個(gè)概念。當(dāng)你對C越來越熟悉時(shí),你會發(fā)現(xiàn),把與指針攪和在一起的“類型”這個(gè)概念分成“指針的類型”和“指針?biāo)赶虻念愋汀眱蓚€(gè)概念,是精通指針的關(guān)鍵點(diǎn)之一。我看了不少書,發(fā)現(xiàn)有些寫得差的書中,就把指針的這兩個(gè)概念攪在一起了,所以看起書來前后矛盾,越看越糊涂。

指針的值

指針的值是指針本身存儲的數(shù)值,這個(gè)值將被編譯器當(dāng)作一個(gè)地址,而不是一個(gè)一般的數(shù)值。在32位程序里,所有類型的指針的值都是一個(gè)32位整數(shù),因?yàn)?2位程序里內(nèi)存地址全都是32位長。?

指針?biāo)赶虻膬?nèi)存區(qū)就是從指針的值所代表的那個(gè)內(nèi)存地址開始,長度為sizeof(指針?biāo)赶虻念愋?的一片內(nèi)存區(qū)。以后,我們說一個(gè)指針的值是XX,就相當(dāng)于說該指針指向了以XX為首地址的一片內(nèi)存區(qū)域;我們說一個(gè)指針指向了某塊內(nèi)存區(qū)域,就相當(dāng)于說該指針的值是這塊內(nèi)存區(qū)域的首地址。?

指針?biāo)赶虻膬?nèi)存區(qū)和指針?biāo)赶虻念愋褪莾蓚€(gè)完全不同的概念。在例一中,指針?biāo)赶虻念愋鸵呀?jīng)有了,但由于指針還未初始化,所以它所指向的內(nèi)存區(qū)是不存在的,或者說是無意義的。?

以后,每遇到一個(gè)指針,都應(yīng)該問問:這個(gè)指針的類型是什么?指針指向的類型是什么?該指針指向了哪里? ?

指針本身所占據(jù)的內(nèi)存區(qū)

指針本身占了多大的內(nèi)存?你只要用函數(shù)sizeof(指針的類型)測一下就知道了。在32位平臺里,指針本身占據(jù)了4個(gè)字節(jié)的長度。
指針本身占據(jù)的內(nèi)存這個(gè)概念在判斷一個(gè)指針表達(dá)式是否是左值時(shí)很有用。  

指針的算術(shù)運(yùn)算

指針可以加上或減去一個(gè)整數(shù)。指針的這種運(yùn)算的意義和通常的數(shù)值的加減運(yùn)算的意義是不一樣的。例如:

char a[20]; int *ptr=a; ... ... ptr++; 在上例中,指針ptr的類型是int*,它指向的類型是int,它被初始化為指向整形變量a。接下來的第3句中,指針ptr被加了1,編譯器是這樣處理的:它把指針ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字節(jié)做單位的,故ptr所指向的地址由原來的變量a的地址向高地址方向增加了4個(gè)字節(jié)。
由于char類型的長度是一個(gè)字節(jié),所以,原來ptr是指向數(shù)組a的第0號單元開始的四個(gè)字節(jié),此時(shí)指向了數(shù)組a中從第4號單元開始的四個(gè)字節(jié)。
我們可以用一個(gè)指針和一個(gè)循環(huán)來遍歷一個(gè)數(shù)組,看例子: ?
int array[20]; int *ptr=array; ... //此處略去為整型數(shù)組賦值的代碼。 ... for(i=0;i<20;i++) { (*ptr)++; ptr++; } 這個(gè)例子將整型數(shù)組中各個(gè)單元的值加1。由于每次循環(huán)都將指針ptr加1,所以每次循環(huán)都能訪問數(shù)組的下一個(gè)單元。再看例子: ?
char a[20]; int *ptr = a; ... ... ptr += 5;

在這個(gè)例子中,ptr被加上了5,編譯器是這樣處理的:將指針ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的單位是字節(jié),故現(xiàn)在的ptr所指向的地址比起加5后的ptr所指向的地址來說,向高地址方向移動了20個(gè)字節(jié)。在這個(gè)例子中,沒加5前的ptr指向數(shù)組a的第0號單元開始的四個(gè)字節(jié),加5后,ptr已經(jīng)指向了數(shù)組a的合法范圍之外了。雖然這種情況在應(yīng)用上會出問題,但在語法上卻是可以的。這也體現(xiàn)出了指針的靈活性。?

如果上例中,ptr是被減去5,那么處理過程大同小異,只不過ptr的值是被減去5乘sizeof(int),新的ptr指向的地址將比原來的ptr所指向的地址向低地址方向移動了20個(gè)字節(jié)。?

總結(jié)一下,一個(gè)指針ptrold加上一個(gè)整數(shù)n后,結(jié)果是一個(gè)新的指針ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值增加了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié)。就是說,ptrnew所指向的內(nèi)存區(qū)將比ptrold所指向的內(nèi)存區(qū)向高地址方向移動了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié)。一個(gè)指針ptrold減去一個(gè)整數(shù)n后,結(jié)果是一個(gè)新的指針ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值減少了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié),就是說,ptrnew所指向的內(nèi)存區(qū)將比ptrold所指向的內(nèi)存區(qū)向低地址方向移動了n乘sizeof(ptrold所指向的類型)個(gè)字節(jié)。

運(yùn)算符&和*

這里&是“取地址”運(yùn)算符,*是...書上叫做“間接運(yùn)算符”。&a的運(yùn)算結(jié)果是一個(gè)指針,指針的類型是a的類型加個(gè)*指針?biāo)赶虻念愋褪莂的類型,指針?biāo)赶虻牡刂仿?#xff0c;那就是a的地址。*p的運(yùn)算結(jié)果就五花八門了??傊?p的結(jié)果是p所指向的東西,這個(gè)東西有這些特點(diǎn):它的類型是p指向的類型,它所占用的地址是p所指向的地址。

int a=12; int b; int *p; int **ptr; p=&a;//&a的結(jié)果是一個(gè)指針,類型是int*,指向的類型是int,指向的地址是a的地址。 *p=24;//*p的結(jié)果,在這里它的類型是int,它所占用的地址是p所指向的地址,顯然,*p就是變量a。 ptr=&p;//&p的結(jié)果是個(gè)指針,該指針的類型是p的類型加個(gè)*,在這里是int**。該指針?biāo)赶虻念愋褪莗的類型,這里是int*。該指針?biāo)赶虻牡刂肪褪侵羔榩自己的地址。 *ptr=&b;//*ptr是個(gè)指針,&b的結(jié)果也是個(gè)指針,且這兩個(gè)指針的類型和所指向的類型是一樣的,所以?amp;b來給*ptr賦值就是毫無問題的了。 **ptr=34;//*ptr的結(jié)果是ptr所指向的東西,在這里是一個(gè)指針,對這個(gè)指針再做一次*運(yùn)算,結(jié)果就是一個(gè)int類型的變量。

指針表達(dá)式

一個(gè)表達(dá)式的最后結(jié)果如果是一個(gè)指針,那么這個(gè)表達(dá)式就叫指針表達(dá)式。下面是一些指針表達(dá)式的例子: ?

int a,b; int array[10]; int *pa; pa=&a;//&a是一個(gè)指針表達(dá)式。 int **ptr=&pa;//&pa也是一個(gè)指針表達(dá)式。 *ptr=&b;//*ptr和&b都是指針表達(dá)式。 pa=array; pa++;//這也是指針表達(dá)式。 char *arr[20]; char **parr=arr;//如果把a(bǔ)rr看作指針的話,arr也是指針表達(dá)式 char *str; str=*parr;//*parr是指針表達(dá)式 str=*(parr+1);//*(parr+1)是指針表達(dá)式 str=*(parr+2);//*(parr+2)是指針表達(dá)式

指針表達(dá)式的結(jié)果是一個(gè)指針。
所以,指針表達(dá)式也具有指針?biāo)哂械乃膫€(gè)要素:指針的類型,指針?biāo)赶虻念愋?#xff0c;指針指向的內(nèi)存區(qū),指針自身占據(jù)的內(nèi)存。

好了,當(dāng)一個(gè)指針表達(dá)式的結(jié)果指針已經(jīng)明確地具有了指針自身占據(jù)的內(nèi)存的話,這個(gè)指針表達(dá)式就是一個(gè)左值,否則就不是一個(gè)左值。 在例七中,&a不是一個(gè)左值,因?yàn)樗€沒有占據(jù)明確的內(nèi)存。*ptr是一個(gè)左值,因?yàn)?ptr這個(gè)指針已經(jīng)占據(jù)了內(nèi)存,其實(shí)*ptr就是指針pa,既然pa已經(jīng)在內(nèi)存中有了自己的位置,那么*ptr當(dāng)然也有了自己的位置。

數(shù)組和指針的關(guān)系

如果對聲明數(shù)組的語句不太明白的話,請參閱我前段時(shí)間貼出的文章<<如何理解c和c++的復(fù)雜類型聲明>>。 數(shù)組的數(shù)組名其實(shí)可以看作一個(gè)指針。看下例:?

int array[10]={0,1,2,3,4,5,6,7,8,9},value; ... ... value=array[0];//也可寫成:value=*array; value=array[3];//也可寫成:value=*(array+3); value=array[4];//也可寫成:value=*(array+4); 上例中,一般而言數(shù)組名array代表數(shù)組本身,類型是int [10],但如果把a(bǔ)rray看做指針的話,它指向數(shù)組的第0個(gè)單元,類型是int *,所指向的類型是數(shù)組單元的類型即int。因此*array等于0就一點(diǎn)也不奇怪了。同理,array+3是一個(gè)指向數(shù)組第3個(gè)單元的指針,所以*(array+3)等于3。其它依此類推。?
char *str[3]={ "Hello,this is a sample!", "Hi,good morning.", "Hello world" }; char s[80]; strcpy(s,str[0]);//也可寫成strcpy(s,*str); strcpy(s,str[1]);//也可寫成strcpy(s,*(str+1)); strcpy(s,str[2]);//也可寫成strcpy(s,*(str+2)); 上例中,str是一個(gè)三單元的數(shù)組,該數(shù)組的每個(gè)單元都是一個(gè)指針,這些指針各指向一個(gè)字符串。把指針數(shù)組名str當(dāng)作一個(gè)指針的話,它指向數(shù)組的第0號單元,它的類型是char**,它指向的類型是char *。

*str也是一個(gè)指針,它的類型是char*,它所指向的類型是char,它指向的地址是字符串"Hello,this is a sample!"的第一個(gè)字符的地址,即'H'的地址。 str+1也是一個(gè)指針,它指向數(shù)組的第1號單元,它的類型是char**,它指向的類型是char *。?

*(str+1)也是一個(gè)指針,它的類型是char*,它所指向的類型是char,它指向"Hi,good morning."的第一個(gè)字符'H',等等。??

下面總結(jié)一下數(shù)組的數(shù)組名的問題。聲明了一個(gè)數(shù)組TYPE array[n],則數(shù)組名稱array就有了兩重含義:第一,它代表整個(gè)數(shù)組,它的類型是TYPE [n];第二,它是一個(gè)指針,該指針的類型是TYPE*,該指針指向的類型是TYPE,也就是數(shù)組單元的類型,該指針指向的內(nèi)存區(qū)就是數(shù)組第0號單元,該指針自己占有單獨(dú)的內(nèi)存區(qū),注意它和數(shù)組第0號單元占據(jù)的內(nèi)存區(qū)是不同的。該指針的值是不能修改的,即類似array++的表達(dá)式是錯誤的。?

在不同的表達(dá)式中數(shù)組名array可以扮演不同的角色。??
在表達(dá)式sizeof(array)中,數(shù)組名array代表數(shù)組本身,故這時(shí)sizeof函數(shù)測出的是整個(gè)數(shù)組的大小。??
在表達(dá)式*array中,array扮演的是指針,因此這個(gè)表達(dá)式的結(jié)果就是數(shù)組第0號單元的值。sizeof(*array)測出的是數(shù)組單元的大小。??

表達(dá)式array+n(其中n=0,1,2,....。)中,array扮演的是指針,故array+n的結(jié)果是一個(gè)指針,它的類型是TYPE*,它指向的類型是TYPE,它指向數(shù)組第n號單元。故sizeof(array+n)測出的是指針類型的大小。?

int array[10]; int (*ptr)[10]; ptr=&array; ptr是一個(gè)指針,它的類型是int (*)[10],他指向的類型是int [10],我們用整個(gè)數(shù)組的首地址來初始化它。在語句ptr=&array中,array代表數(shù)組本身。?
本節(jié)中提到了函數(shù)sizeof(),那么我來問一問,sizeof(指針名稱)測出的究竟是指針自身類型的大小呢還是指針?biāo)赶虻念愋偷拇笮?#xff1f;答案是前者。例如:
int (*ptr)[10]; 則在32位程序中,有: ?
sizeof(int(*)[10])==4 sizeof(int [10])==40 sizeof(ptr)==4 實(shí)際上,sizeof(對象)測出的都是對象自身的類型的大小,而不是別的什么類型的大小。  

指針和結(jié)構(gòu)類型的關(guān)系

可以聲明一個(gè)指向結(jié)構(gòu)類型對象的指針。 ?

struct MyStruct { int a; int b; int c; } MyStruct ss={20,30,40};//聲明了結(jié)構(gòu)對象ss,并把ss的三個(gè)成員初始化為20,30和40。 MyStruct *ptr=&ss;//聲明了一個(gè)指向結(jié)構(gòu)對象ss的指針。它的類型是 MyStruct*,它指向的類型是MyStruct。 int *pstr=(int*)&ss;//聲明了一個(gè)指向結(jié)構(gòu)對象ss的指針。但是它的類型和它指向的類型和ptr是不同的。 請問怎樣通過指針ptr來訪問ss的三個(gè)成員變量? ?
答案: ptr->a; ptr->b; ptr->c; 又請問怎樣通過指針pstr來訪問ss的三個(gè)成員變量? ?
答案: *pstr;//訪問了ss的成員a。 *(pstr+1);//訪問了ss的成員b。 *(pstr+2)//訪問了ss的成員c。 呵呵,雖然我在我的MSVC++6.0上調(diào)式過上述代碼,但是要知道,這樣使用pstr來訪問結(jié)構(gòu)成員是不正規(guī)的,為了說明為什么不正規(guī),讓我們看看怎樣通過指針來訪問數(shù)組的各個(gè)單元:
int array[3]={35,56,37}; int *pa=array; 通過指針pa訪問數(shù)組array的三個(gè)單元的方法是: ?
*pa;//訪問了第0號單元 *(pa+1);//訪問了第1號單元 *(pa+2);//訪問了第2號單元 從格式上看倒是與通過指針訪問結(jié)構(gòu)成員的不正規(guī)方法的格式一樣。

所有的C/C++編譯器在排列數(shù)組的單元時(shí),總是把各個(gè)數(shù)組單元存放在連續(xù)的存儲區(qū)里,單元和單元之間沒有空隙。但在存放結(jié)構(gòu)對象的各個(gè)成員時(shí),不同編譯器可能會需要字對齊或雙字對齊或者是別的什么對齊,需要在相鄰兩個(gè)成員之間加若干個(gè)“填充字節(jié)”,這就導(dǎo)致各個(gè)成員之間可能會有若干個(gè)字節(jié)的空隙。這就是內(nèi)存對齊。

所以,在例十二中,即使*pstr訪問到了結(jié)構(gòu)對象ss的第一個(gè)成員變量a,也不能保證*(pstr+1)就一定能訪問到結(jié)構(gòu)成員b。因?yàn)槌蓡Ta和成員b之間可能會有若干填充字節(jié),說不定*(pstr+1)就正好訪問到了這些填充字節(jié)呢。這也證明了指針的靈活性。要是你的目的就是想看看各個(gè)結(jié)構(gòu)成員之間到底有沒有填充字節(jié),嘿,這倒是個(gè)不錯的方法。?通過指針訪問結(jié)構(gòu)成員的正確方法應(yīng)該是象例十二中使用指針ptr的方法。

指針和函數(shù)的關(guān)系(函數(shù)指針)

可以把一個(gè)指針聲明成為一個(gè)指向函數(shù)的指針。 ?

int fun1(char*,int); int (*pfun1)(char*,int); pfun1=fun1; .... .... int a = (*pfun1)("abcdefg",7);//通過函數(shù)指針調(diào)用函數(shù)?;蛘?int a = pfun1("abcdefg",7)。但是推薦使用int a = (*pfun1)("abcdefg",7) 與數(shù)據(jù)一樣,函數(shù)也有地址,函數(shù)的地址就是內(nèi)存中存放函數(shù)語言代碼的起始地址。函數(shù)指針就是指向這個(gè)地址。函數(shù)指針?biāo)赶虻念愋?#xff0c;就是函數(shù)本身。我們知道,指針?biāo)赶蝾愋痛砹酥羔標(biāo)赶虻膬?nèi)存區(qū)域的大小。所以函數(shù)指針?biāo)赶虻念愋?#xff0c;就是函數(shù)在內(nèi)存中所占據(jù)內(nèi)存的大小。知道了函數(shù)的起始地址和大小,所以函數(shù)指針可以很輕易的代替函數(shù)完成函數(shù)調(diào)用。

#include <string.h> #include <stdio.h> typedef int* PINNT; #define PP int* int funcA(int a,int b); int funcB(int* a,int *b); int main(int argc, char *argv[]) { int (*func)(int,int); //func = &funcA; func = funcA; //兩種賦值給函數(shù)指針的方法都可以 printf("%d",func(1,10)); //printf("%d",(*func)(1,10)); //兩種調(diào)用函數(shù)指針的方法都可以 //兩種賦值方法和兩種調(diào)用方法可以任選一種組合 } int funcA(int a,int b) { return a + b; } int funcB(int* a,int *b) { (*a) = (*a) + (*b); return 1; } #include <iostream> using namespace std;int test(int, int); //自定義一個(gè)新類型,類型名是NewType,自身類型是int ()(int,int) typedef int (NewType)(int,int);int main(void) {int (*pFunc)(int,int); //定義一個(gè)函數(shù)指針 pFuncpFunc = test; //或者 pFunc = &test;cout<<(*pFunc)(10,20)<<endl; // 或者 pFunc(10,20);/* 注意 和 函數(shù)指針 區(qū)別 */NewType* newType = test; //或者 NewType* newType = &test;cout<<newType(30,40)<<endl; //或者 (*newType)(30,40); return 0; }int test(int a, int b) {cout<<"a:"<<a<<endl<<"b:"<<b<<endl;return a+b; }

一、普通函數(shù)指針

通常我們所說的函數(shù)指針指的是指向一般普通函數(shù)的指針。和其他指針一樣,函數(shù)指針指向某種特定類型,所有被同一指針運(yùn)用的函數(shù)必須具有相同的形參類型和返回類型。

int (*pf)(int, int); // 聲明函數(shù)指針

這里,pf指向的函數(shù)類型是int (int, int),即函數(shù)的參數(shù)是兩個(gè)int型,返回值也是int型。

注意:*pf兩端的括號必不可少,如果不寫這對括號,則pf是一個(gè)返回值為int指針的函數(shù)。

二、成員函數(shù)指針

定義:typedef 返回類型(類名::*新類型)(參數(shù)表)

一句話,使用類成員函數(shù)指針必須有“->*”或“.*”的調(diào)用。

成員函數(shù)指針(member function pointer)是指可以指向類的非靜態(tài)成員函數(shù)的指針。類的靜態(tài)成員不屬于任何對象,因此無須特殊的指向靜態(tài)成員的指針,指向靜態(tài)成員的指針與普通指針沒有什么區(qū)別。與普通函數(shù)指針不同的是,成員函數(shù)指針不僅要指定目標(biāo)函數(shù)的形參列表和返回類型,還必須指出成員函數(shù)所屬的類。因此,我們必須在*之前添加classname::以表示當(dāng)前定義的指針指向classname的成員函數(shù):

int (A::*pf)(int, int); // 聲明一個(gè)成員函數(shù)指針

同理,這里A::*pf兩端的括號也是必不可少的,如果沒有這對括號,則pf是一個(gè)返回A類數(shù)據(jù)成員(int型)指針的函數(shù)。注意:和普通函數(shù)指針不同的是,在成員函數(shù)指向該成員的指針之間不存在自動轉(zhuǎn)換規(guī)則。所以,必須顯式地使用取址運(yùn)算符(&)

pf = &A::add; // 正確:必須顯式地使用取址運(yùn)算符(&) pf = A::add; // 錯誤

當(dāng)我們初始化一個(gè)成員函數(shù)指針時(shí),其指向了類的某個(gè)成員函數(shù),但并沒有指定該成員所屬的對象——直到使用成員函數(shù)指針時(shí),才提供成員所屬的對象。下面是一個(gè)成員函數(shù)指針的使用示例:
class A; typedef int (A::*pClassFun)(int, int); // 成員函數(shù)指針類型class A{ public:int add(int m, int n){cout << m << " + " << n << " = " << m+n << endl;return m+n;}int mns(int m, int n){cout << m << " - " << n << " = " << m-n << endl;return m-n;}int mul(int m, int n){cout << m << " * " << n << " = " << m*n << endl;return m*n;}int dev(int m, int n){cout << m << " / " << n << " = " << m/n << endl;return m/n;}int call(pClassFun fun, int m, int n){// 類內(nèi)部接口return (this->*fun)(m, n);} };int call(A obj, pClassFun fun, int m, int n) { // 類外部接口return (obj.*fun)(m, n); }int call(A* pObj,pClassFun fun, int m, int n) { // 類外部接口return (pObj->*fun)(m, n); }int main() {A a;cout << "member function 'call':" << endl;a.call(&A::add, 8, 4);a.call(&A::mns, 8, 4);a.call(&A::mul, 8, 4);a.call(&A::dev, 8, 4);cout << "external function 'call(A obj, pClassFun fun, int m, int n)':" << endl;call(a, &A::add, 9, 3);call(a, &A::mns, 9, 3);call(a, &A::mul, 9, 3);call(a, &A::dev, 9, 3);cout << "external function 'call(A* pObj,pClassFun fun, int m, int n)':" << endl;call(&a, &A::add, 9, 3);call(&a, &A::mns, 9, 3);call(&a, &A::mul, 9, 3);call(&a, &A::dev, 9, 3);return 0; } 如示例所示,我們一樣可以使用typedef定義成員函數(shù)指針的類型別名。另外,我們需要留意函數(shù)指針的使用方法:對于普通函數(shù)指針,是這樣使用(*pf)(arguments),因?yàn)橐{(diào)用函數(shù),必須先解引用函數(shù)指針,而函數(shù)調(diào)用運(yùn)算符()的優(yōu)先級較高,所以(*pf)的括號必不可少;對于成員函數(shù)指針,唯一的不同是需要在某一對象上調(diào)用函數(shù),所以只需要加上成員訪問符即可:
(obj.*pf)(arguments) // obj 是對象 (objptr->*pf)(arguments) // objptr是對象指針

三、函數(shù)表驅(qū)動

對于普通函數(shù)指針和指向成員函數(shù)的指針來說,一種常見的用法就是將其存入一個(gè)函數(shù)表(function table)當(dāng)中。當(dāng)程序需要執(zhí)行某個(gè)特定的函數(shù)時(shí),就從表中查找對應(yīng)的函數(shù)指針,用該指針來調(diào)用相應(yīng)的程序代碼,這個(gè)就是函數(shù)指針在表驅(qū)動法中的應(yīng)用。

表驅(qū)動法(Table-Driven Approach)就是用查表的方法獲取信息。通常,在數(shù)據(jù)不多時(shí)可用邏輯判斷語句(if…else或switch…case)來獲取信息;但隨著數(shù)據(jù)的增多,邏輯語句會越來越長,此時(shí)表驅(qū)動法的優(yōu)勢就體現(xiàn)出來了。

#include<iostream> #include<string> #include<map> using namespace std; class A; typedef int (A::*pClassFun)(int, int); class A{ public: A(){? // 構(gòu)造函數(shù),初始化表 table["+"] = &A::add; table["-"] = &A::mns; table["*"] = &A::mul; table["/"] = &A::dev; } int add(int m, int n){ cout << m << " + " << n << " = " << m+n << endl; return m+n; } int mns(int m, int n){ cout << m << " - " << n << " = " << m-n << endl; return m-n; } int mul(int m, int n){ cout << m << " * " << n << " = " << m*n << endl; return m*n; } int dev(int m, int n){ cout << m << " / " << n << " = " << m/n << endl; return m/n; } // 查找表,調(diào)用相應(yīng)函數(shù) int call(string s, int m, int n){ return (this->*table[s])(m, n); } private: map<string, pClassFun> table; // 函數(shù)表 }; // 測試 int main() { A a; a.call("+", 8, 2); a.call("-", 8, 2); a.call("*", 8, 2); a.call("/", 8, 2); return 0; } 使用類型定義?
  可以用類型定義來隱藏復(fù)雜的成員指針語法。例如,下面的語句定義了PMA是一個(gè)指向A成員函數(shù)的指針,函數(shù)返回?zé)o類型值,函數(shù)參數(shù)類型為char * 和 const char *:?
  typedef void(A::*PMA)(char *, const char *);?
  PMA pmf= &A::strcat; // pmf是PMF類型(類A成員指針)的變量?
  下文會看到使用類型定義特別有利于聲明成員指針數(shù)組。?
A a1, a2; A *p= &a1; //創(chuàng)建指向A的指針 //創(chuàng)建指向成員的指針并初始化 void (A::*pmf)(char *, const char *) = &A::strcpy; //要將成員函數(shù)綁定到pmf,必須定義呼叫的對象。 //可以用*號引導(dǎo):void dispatcher(A a, void (A::*pmf)(char *, const char *)) {char str[4];(a.*pmf)(str, “abc”); //將成員函數(shù)綁定到pmf }//或用A的指針表達(dá)方式指向成員指針: void dispatcher(A * p, void (A::*pmf)(char *, const char *)) {char str[4]; (p->*pmf)(str, “abc”); }//函數(shù)的調(diào)用方法為: dispatcher(a, pmf); // .* 方式1 dispatcher(&a, pmf); // ->* 方式2 高級使用技巧?
  以上是成員函數(shù)的基本知識。現(xiàn)在介紹它的高級使用技巧。?
   成員指針數(shù)組 ?
  在下例,聲明了一個(gè)含有二個(gè)成員指針的數(shù)組,并分配類的成員函數(shù)地址給成員指針:?
  PMA pmf[2]= {&A::strcpy, &A::strcat};?
也就是
????? void (A::*PMA[2])(char *, const char *)= {&A::strcpy, &A::strcat};?
  這樣的數(shù)組在菜單驅(qū)動應(yīng)用中很有用。選擇菜單項(xiàng)后,應(yīng)用將調(diào)用相應(yīng)的回叫函數(shù),如下所示:?
enum MENU_OPTIONS { COPY, CONCAT }; int main() {MENU_OPTIONS option; char str[4];//從外部資源讀取選項(xiàng)switch (option){case COPY: (pa->*pmf[COPY])(str, “abc”); break; case CONCAT: (pa->*pmf[CONCAT])(str, “abc”); break; //… } } Const 類型的成員函數(shù)?
  成員指針的類型應(yīng)該與成員函數(shù)類型一致。上面例子中的pmf 可以指向A的任意函數(shù),只要該函數(shù)不是const類型。如下所示,如果將touppercase()的地址分配給pmf,將導(dǎo)致編譯出錯,因?yàn)閠ouppercase() 的類型是const。?
class A { public: void strpcy(char *, const char *); void strcat(char *, const char *); void touppercase(char *, const char*) const; };pmf=&A::touppercase; //出錯,類型不匹配//解決的方法是聲明一個(gè)const類型的成員指針: void (A::pcmf)(char *, const char *) const;pcmf=&A::touppercase; // 現(xiàn)在可以了

一、最簡單的函數(shù)指針

  變量都包括聲明和賦值,指針不例外,函數(shù)指針也不例外。我們來看一個(gè)簡單的函數(shù):

void add(int a, int b){cout << a + b << endl; }

一個(gè)簡單的加法計(jì)算并輸出到命令行的函數(shù)。那么如何通過函數(shù)指針來調(diào)用它呢?

1、聲明:

void (*p1)(int a, int b);

函數(shù)指針的聲明很簡單,基本就是通過一個(gè)指針把函數(shù)名替換。指針p1的類型為void (*) (int a,int b),表明指針是一個(gè)指向某個(gè)函數(shù)的指針,指針指向的類型為void () (int a,int b)

2、賦值:

p1 = add;

3、也可以直接定義:

void (*p1)(int a, int b) = add;  

注意,函數(shù)void add(int a,int b)的函數(shù)名add就是函數(shù)的地址。將地址add賦值給指針p1,那么就可以通過函數(shù)指針p1直接調(diào)用函數(shù)了。

4、調(diào)用:

(*p1)(1, 2); p1(1, 2); 注意!出于歷史原因以上2種方式都是可以調(diào)用函數(shù)的。

二、包含多個(gè)函數(shù)指針的數(shù)組

  有時(shí)候有這種情況,有一個(gè)數(shù)組,數(shù)組中的每個(gè)元素都是一個(gè)函數(shù)指針,該怎么定義這個(gè)數(shù)組呢?

  1、解釋*p[n]和(*p)[n]

  我們知道,[]運(yùn)算符的優(yōu)先級要高于*,所以,p[3]表示含有3個(gè)元素的數(shù)組,而*p[3] 前面的 " * " 指明了數(shù)組中元素的類型,即*p[3]表示一個(gè)指向3個(gè)指針的數(shù)組。?

  p[3]表示含有3個(gè)元素的數(shù)組,那么(*p)[3]就是用 *p 替換了 p,很容易想到,(*p)[3]?表示指向一個(gè)包含3個(gè)元素的數(shù)組的指針。

  2、聲明:

void (*p2[2])(int a, int b);

  數(shù)組名為p2,數(shù)組大小為2,數(shù)組中元素類型為void (*)(int a, int b),表明元素是一個(gè)指向某個(gè)函數(shù)的指針,指針指向的類型為void () (int a,int b)。

  3、賦值: 

p2[1] = add;

  理解上跟上面是一樣的。

  4、調(diào)用: 

p2[1](2,3); (*p2[1])(3,4);   同樣是2種方式都可以。

三、指向“包含多個(gè)函數(shù)指針的數(shù)組“的指針

  這個(gè)標(biāo)題好像有點(diǎn)拗口。簡而言之,這個(gè)指針指向上文中的 “包含多個(gè)函數(shù)指針的數(shù)組” 。其實(shí)很簡單,說白了,就是把上文中的p2用一個(gè)指針來代替。

  1、聲明:

void (*(*p3)[2])(int a, int b);

?  可以看到,無非就是把p2用*p3代替。

  2、賦值,注意,既然是指針,使用前必須初始化:

p3 = &p2; (*p3)[1] = add;

  注意!既然實(shí)質(zhì)上就是把p2用*p3代替,c++11可以很簡單的這樣直接定義:auto p3 = &p2; 代替了void (*(*p3)[2])(int a, int b)= &p2;

  3、調(diào)用:

(*p3)[1](1, 2); (*(*p3)[1])(1, 2);


1.?????定義

每一個(gè)函數(shù)都占用一段內(nèi)存單元,它們有一個(gè)起始地址,指向函數(shù)入口地址的指針稱為函數(shù)指針。

2.?????語法

指向函數(shù)的指針變量的一般定義形式為:數(shù)據(jù)類型?(*指針變量名)(參數(shù)表);

3.?????說明

? ? 1)?函數(shù)指針的定義形式中的數(shù)據(jù)類型是指函數(shù)的返回值的類型。

? ? 2)?區(qū)分下面兩個(gè)語句:

? ? ? ? int (*p)(int a, int b); //p是一個(gè)指向函數(shù)的指針變量,所指函數(shù)的返回值類型為整型

? ? ? ? int *p(int a, int b); //p是函數(shù)名,此函數(shù)的返回值類型為整型指針

? ? 3)?指向函數(shù)的指針變量不是固定指向哪一個(gè)函數(shù),只是定義一個(gè)這樣類型的變量,專門用來存放函數(shù)入口地址;程序中把哪一個(gè)函數(shù)的地址賦給它,它就指向哪一個(gè)函數(shù)。

? ? 4)?在給函數(shù)指針變量賦值時(shí),只需給出函數(shù)名,而不必給出參數(shù)。

? ? ? ? 如函數(shù)max的原型為:int max(int x, int y);?指針p的定義為:int (*p)(int a, int b);?則p = max;的作用是將函數(shù)max的入口地址賦給指針變量p。

? ? ? ? 這時(shí),p就是指向函數(shù)max的指針變量,也就是p和max都指向函數(shù)的開頭。

? ? 5)?在一個(gè)程序中,指針變量p可以先后指向不同的函數(shù),但一個(gè)函數(shù)不能賦給一個(gè)不一致的函數(shù)指針(即不能讓一個(gè)函數(shù)指針指向與其類型不一致的函數(shù))。

? ? ? ? 如有如下的函數(shù):int fn1(int x, int y);?int fn2(int x);

? ? ? ? 定義如下的函數(shù)指針:int (*p1)(int a, int b);?int (*p2)(int a);

? ? ? ? 則

? ? ? ? p1 = fn1; //正確

? ? ? ? p2 = fn2; //正確

? ? ? ? p1 = fn2; //產(chǎn)生編譯錯誤

? ? 6)?定義了一個(gè)函數(shù)指針并讓它指向了一個(gè)函數(shù)后,對函數(shù)的調(diào)用可以通過函數(shù)名調(diào)用,也可以通過函數(shù)指針調(diào)用(即用指向函數(shù)的指針變量調(diào)用)。

? ? ? ? 如語句:c = (*p)(a, b); //表示調(diào)用由p指向的函數(shù)(max),實(shí)參為a,b,函數(shù)調(diào)用結(jié)束后得到的函數(shù)值賦給c。

? ? 7)?函數(shù)指針只能指向函數(shù)的入口處,而不可能指向函數(shù)中間的某一條指令。不能用*(p+1)來表示函數(shù)的下一條指令。

? ? 8)?函數(shù)指針變量常用的用途之一是把指針作為參數(shù)傳遞到其他函數(shù)。

#include <stdlib.h> #include <stdio.h> #include <conio.h> using namespace std;int max(int, int); //在“聲明”時(shí)形參名可以省略,“定義”時(shí)只要形參類型相同,就認(rèn)為是同一個(gè)函數(shù) int min(int, int); int add(int x, int y); void process(int i, int j, int (*p)(int, int)); //應(yīng)用函數(shù)指針int max(int x, int y){return x > y ? x : y;} //定義時(shí)指定 形參名 int min(int x, int y){return x > y ? y : x;} int add(int x, int y){return x + y;}void process(int i, int j, int (*p)(int a, int b)) {/* 函數(shù)指針帶不帶*都可以操作所指向的函數(shù) 。 但是 最好 加上*號 以用來指示這是一個(gè)指針 */cout<<p(i, j)<<endl; //直接 不使用 * 操作cout<<(*p)(i, j)<<endl; // 使用 * 也可以操作cout<<"************************"<<endl; }int main(void) {int x=100, y=200;//cout<<"input x and y value(x y):";//cin>>x>>y;cout<<"Max is: ";process(x, y, max);cout<<"Min is: ";process(x, y, min);cout<<"Add is: ";process(x, y, add);getch();return 0; }





總結(jié)

以上是生活随笔為你收集整理的C++函数指针详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。