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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++函数指针详解

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

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

指針的概念

指針是一個特殊的變量,它里面存儲的數值被解釋成為內存里的一個地址。要搞清一個指針需要搞清指針的四方面的內容:指針的類型,指針所指向的類型,指針的值或者叫指針所指向的內存區,還有指針本身所占據的內存區。讓我們分別說明。?

先聲明幾個指針放著做例子: ?

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

指針的類型

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

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

指針所指向的類型

當你通過指針來訪問指針所指向的內存區時,指針所指向的類型決定了編譯器將把那片內存區里的內容當做什么來看待。
從語法上看,你只須把指針聲明語句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針所指向的類型。例如: ?

int *ptr; //指針所指向的類型是int char *ptr; //指針所指向的的類型是char int **ptr; //指針所指向的的類型是 int * int (*ptr)[3]; //指針所指向的的類型是 int()[3] int *(*ptr)[4]; //指針所指向的的類型是 int *()[4] 在指針的算術運算中,指針所指向的類型有很大的作用。??

指針的類型(即指針本身的類型)指針所指向的類型是兩個概念。當你對C越來越熟悉時,你會發現,把與指針攪和在一起的“類型”這個概念分成“指針的類型”和“指針所指向的類型”兩個概念,是精通指針的關鍵點之一。我看了不少書,發現有些寫得差的書中,就把指針的這兩個概念攪在一起了,所以看起書來前后矛盾,越看越糊涂。

指針的值

指針的值是指針本身存儲的數值,這個值將被編譯器當作一個地址,而不是一個一般的數值。在32位程序里,所有類型的指針的值都是一個32位整數,因為32位程序里內存地址全都是32位長。?

指針所指向的內存區就是從指針的值所代表的那個內存地址開始,長度為sizeof(指針所指向的類型)的一片內存區。以后,我們說一個指針的值是XX,就相當于說該指針指向了以XX為首地址的一片內存區域;我們說一個指針指向了某塊內存區域,就相當于說該指針的值是這塊內存區域的首地址。?

指針所指向的內存區和指針所指向的類型是兩個完全不同的概念。在例一中,指針所指向的類型已經有了,但由于指針還未初始化,所以它所指向的內存區是不存在的,或者說是無意義的。?

以后,每遇到一個指針,都應該問問:這個指針的類型是什么?指針指向的類型是什么?該指針指向了哪里? ?

指針本身所占據的內存區

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

指針的算術運算

指針可以加上或減去一個整數。指針的這種運算的意義和通常的數值的加減運算的意義是不一樣的。例如:

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

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

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

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

運算符&和*

這里&是“取地址”運算符,*是...書上叫做“間接運算符”。&a的運算結果是一個指針,指針的類型是a的類型加個*指針所指向的類型是a的類型,指針所指向的地址嘛,那就是a的地址。*p的運算結果就五花八門了。總之*p的結果是p所指向的東西,這個東西有這些特點:它的類型是p指向的類型,它所占用的地址是p所指向的地址。

int a=12; int b; int *p; int **ptr; p=&a;//&a的結果是一個指針,類型是int*,指向的類型是int,指向的地址是a的地址。 *p=24;//*p的結果,在這里它的類型是int,它所占用的地址是p所指向的地址,顯然,*p就是變量a。 ptr=&p;//&p的結果是個指針,該指針的類型是p的類型加個*,在這里是int**。該指針所指向的類型是p的類型,這里是int*。該指針所指向的地址就是指針p自己的地址。 *ptr=&b;//*ptr是個指針,&b的結果也是個指針,且這兩個指針的類型和所指向的類型是一樣的,所以?amp;b來給*ptr賦值就是毫無問題的了。 **ptr=34;//*ptr的結果是ptr所指向的東西,在這里是一個指針,對這個指針再做一次*運算,結果就是一個int類型的變量。

指針表達式

一個表達式的最后結果如果是一個指針,那么這個表達式就叫指針表達式。下面是一些指針表達式的例子: ?

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

指針表達式的結果是一個指針。
所以,指針表達式也具有指針所具有的四個要素:指針的類型,指針所指向的類型,指針指向的內存區,指針自身占據的內存。

好了,當一個指針表達式的結果指針已經明確地具有了指針自身占據的內存的話,這個指針表達式就是一個左值,否則就不是一個左值。 在例七中,&a不是一個左值,因為它還沒有占據明確的內存。*ptr是一個左值,因為*ptr這個指針已經占據了內存,其實*ptr就是指針pa,既然pa已經在內存中有了自己的位置,那么*ptr當然也有了自己的位置。

數組和指針的關系

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

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); 上例中,一般而言數組名array代表數組本身,類型是int [10],但如果把array看做指針的話,它指向數組的第0個單元,類型是int *,所指向的類型是數組單元的類型即int。因此*array等于0就一點也不奇怪了。同理,array+3是一個指向數組第3個單元的指針,所以*(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是一個三單元的數組,該數組的每個單元都是一個指針,這些指針各指向一個字符串。把指針數組名str當作一個指針的話,它指向數組的第0號單元,它的類型是char**,它指向的類型是char *。

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

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

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

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

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

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

指針和結構類型的關系

可以聲明一個指向結構類型對象的指針。 ?

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

所有的C/C++編譯器在排列數組的單元時,總是把各個數組單元存放在連續的存儲區里,單元和單元之間沒有空隙。但在存放結構對象的各個成員時,不同編譯器可能會需要字對齊或雙字對齊或者是別的什么對齊,需要在相鄰兩個成員之間加若干個“填充字節”,這就導致各個成員之間可能會有若干個字節的空隙。這就是內存對齊。

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

指針和函數的關系(函數指針)

可以把一個指針聲明成為一個指向函數的指針。 ?

int fun1(char*,int); int (*pfun1)(char*,int); pfun1=fun1; .... .... int a = (*pfun1)("abcdefg",7);//通過函數指針調用函數。或者 int a = pfun1("abcdefg",7)。但是推薦使用int a = (*pfun1)("abcdefg",7) 與數據一樣,函數也有地址,函數的地址就是內存中存放函數語言代碼的起始地址函數指針就是指向這個地址。函數指針所指向的類型,就是函數本身。我們知道,指針所指向類型代表了指針所指向的內存區域的大小。所以函數指針所指向的類型,就是函數在內存中所占據內存的大小。知道了函數的起始地址和大小,所以函數指針可以很輕易的代替函數完成函數調用。

#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; //兩種賦值給函數指針的方法都可以 printf("%d",func(1,10)); //printf("%d",(*func)(1,10)); //兩種調用函數指針的方法都可以 //兩種賦值方法和兩種調用方法可以任選一種組合 } 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); //自定義一個新類型,類型名是NewType,自身類型是int ()(int,int) typedef int (NewType)(int,int);int main(void) {int (*pFunc)(int,int); //定義一個函數指針 pFuncpFunc = test; //或者 pFunc = &test;cout<<(*pFunc)(10,20)<<endl; // 或者 pFunc(10,20);/* 注意 和 函數指針 區別 */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; }

一、普通函數指針

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

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

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

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

二、成員函數指針

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

一句話,使用類成員函數指針必須有“->*”或“.*”的調用。

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

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

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

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

當我們初始化一個成員函數指針時,其指向了類的某個成員函數,但并沒有指定該成員所屬的對象——直到使用成員函數指針時,才提供成員所屬的對象。下面是一個成員函數指針的使用示例:
class A; typedef int (A::*pClassFun)(int, int); // 成員函數指針類型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){// 類內部接口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定義成員函數指針的類型別名。另外,我們需要留意函數指針的使用方法:對于普通函數指針,是這樣使用(*pf)(arguments),因為要調用函數,必須先解引用函數指針,而函數調用運算符()的優先級較高,所以(*pf)的括號必不可少;對于成員函數指針,唯一的不同是需要在某一對象上調用函數,所以只需要加上成員訪問符即可:
(obj.*pf)(arguments) // obj 是對象 (objptr->*pf)(arguments) // objptr是對象指針

三、函數表驅動

對于普通函數指針和指向成員函數的指針來說,一種常見的用法就是將其存入一個函數表(function table)當中。當程序需要執行某個特定的函數時,就從表中查找對應的函數指針,用該指針來調用相應的程序代碼,這個就是函數指針在表驅動法中的應用。

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

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

一、最簡單的函數指針

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

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

一個簡單的加法計算并輸出到命令行的函數。那么如何通過函數指針來調用它呢?

1、聲明:

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

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

2、賦值:

p1 = add;

3、也可以直接定義:

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

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

4、調用:

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

二、包含多個函數指針的數組

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

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

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

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

  2、聲明:

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

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

  3、賦值: 

p2[1] = add;

  理解上跟上面是一樣的。

  4、調用: 

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

三、指向“包含多個函數指針的數組“的指針

  這個標題好像有點拗口。簡而言之,這個指針指向上文中的 “包含多個函數指針的數組” 。其實很簡單,說白了,就是把上文中的p2用一個指針來代替。

  1、聲明:

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

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

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

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

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

  3、調用:

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


1.?????定義

每一個函數都占用一段內存單元,它們有一個起始地址,指向函數入口地址的指針稱為函數指針。

2.?????語法

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

3.?????說明

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

? ? 2)?區分下面兩個語句:

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

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

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

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

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

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

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

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

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

? ? ? ? 則

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

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

? ? ? ? p1 = fn2; //產生編譯錯誤

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

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

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

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

#include <stdlib.h> #include <stdio.h> #include <conio.h> using namespace std;int max(int, int); //在“聲明”時形參名可以省略,“定義”時只要形參類型相同,就認為是同一個函數 int min(int, int); int add(int x, int y); void process(int i, int j, int (*p)(int, int)); //應用函數指針int max(int x, int y){return x > y ? x : y;} //定義時指定 形參名 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)) {/* 函數指針帶不帶*都可以操作所指向的函數 。 但是 最好 加上*號 以用來指示這是一個指針 */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; }





總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。