(九十九)函数指针
来源:互联网 发布:macd指标通用源码 编辑:程序博客网 时间:2024/05/15 19:49
与数据项(比如int、string、char等)相似,函数也有地址。
函数的地址是存储其机器语言代码的内存的开始地址(不懂)。
这些地址对用户而言,没什么用,也不重要,但对程序而言,却很有用。
例如,可以编写将另外一个函数的地址作为参数的函数。这样,第一个函数将能够找到第二个函数,并运行它。
与直接调用第二个函数相比,这种方法很笨拙,但他允许在不同的时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数。
函数指针和指针函数的区别:
函数指针:指向某个函数的指针(即这个指针指向函数的地址);
指针函数:返回值是指针的函数
函数指针的基础知识:
函数的地址:
函数名 就是函数的地址(像字符串一样);
声明函数指针:
有些像函数返回指针,但是要把*和函数名用括号括起来。例如:函数类型 (*函数名) (参数);
注意:①声明的函数指针,要和函数的特征标(例如类型、函数的参数类型)要相符;
②*函数名 此时是函数,而 函数名 此时是指针。
函数指针作为参数时:
函数类型 函数名(函数类型 (*函数名)(函数指针的参数类型) );
注:蓝色部分为函数指针作为参数时的写法。
调用函数指针时:
和声明函数指针类似,用函数指针的 (*函数名) 取代普通函数 函数名 即可。
如:(*函数名)(函数指针的参数) 这样。
也可以像使用普通函数那样,通过 函数名(参数) 这样调用函数,C++是允许这样做的。
给函数指针赋值时:
首先,函数指针和函数的特征标要相符。
然后例如代码节选:
void ab(int);//函数原型
void(*c)(int);//函数指针原型
c = ab; //让函数指针c指向函数ab。这行代码一般在函数里
这样。
无返回值、将函数指针赋值、声明函数指针、调用函数指针,如代码:
#include<iostream>using namespace std;void ab(int);//函数1void ef(int);//函数2void(*c)(int);//声明函数指针,注意,这行代码放在main()函数内也可以int main(){int a = 5;c = ab; //函数指针指向函数1c(a);//调用函数指针(即执行函数1)c = ef;//函数指针指向函数2(*c)(a);//调用函数指针(此时执行函数2),注意c和(*c)是等价的,不能省略括号system("pause");return 0;}void ab(int m){cout << "m = " << m << endl;}void ef(int m){cout << "m = " << m * 2 << endl;}
输出:
m = 5m = 10请按任意键继续. . .
解释:
①这行代码就是函数指针指向不同函数时,同样的变量作为参数,却执行不同的函数。
有返回值、将函数指针作为函数的参数、不同情况下指向不同的函数、使用函数的地址,如代码:
#include<iostream>using namespace std;int ab(int);//函数1int ef(int);//函数2int c(int(*x)(int),int y);//引入函数指针作为函数c的参数int main(){int a = 5;int m, n;m = c(ab, a);//调用的时候,指定ab函数作为参数n = c(ef, a);//调用的时候,制定ef为指定的函数cout << "m = " << m << endl;cout << "n = " << n << endl;system("pause");return 0;}int ab(int m){return m + 3;}int ef(int m){return m * 3;}int c(int(*x)(int),int y)//x指向某个函数,y为函数将使用的参数。因此,在调用的时候,需要指定使用哪个函数。{return x(y);//返回 调用指针指向的函数(参数)的返回值}
输出:
m = 8n = 15请按任意键继续. . .
总结:
①以上两部分代码,涉及到了函数的地址(即函数名)、声明函数指针、函数指针作为参数、调用函数指针、给函数指针赋值 这五个概念;
②可以在函数内部声明函数指针,然后根据变量的不同,让指针指向其他不同的函数,再执行对应的函数,并将对应函数的返回值作为函数的返回值。
如代码:
int c(int y)//x指向某个函数,y为函数将使用的参数。因此,在调用的时候,需要指定使用哪个函数。
{
int(*x)(int);//声明函数指针x
if(y<10)x=ab; else x=ef;//不同参数y,让x指向不同的指针
return x(y);//调用函数指针,y作为参数,返回值作为函数c的返回值
}
③A函数指针作为参数时,可以传递不同的函数名(例如B或者C)作为参数,使得在A函数中根据传递的函数名的不同,执行不同的函数(B或者C)。
当函数返回值为指针的函数指针:
函数
示例函数原型
普通函数
int abc(int);
函数指针
int (*m) (int);
返回值为指针的函数
int* abc (int);
返回值为指针的函数指针
int* (*m) (int);
可以发现,无论是指向普通函数,还是指向返回值为指针的函数,函数指针都是用(*指针名)替代函数名。就像(*m)替代abc 一样。
不同参数让指针指向不同的函数,代码:
#include<iostream>using namespace std;int*m(int);int*n(int);int main(){int*(*abc)(int);//创建一个函数指针,面对对象是返回值是指针的函数int *l;//创建一个指针,将函数的返回值(指针)赋值给他。for (int i = 0;i < 5;i++){if (i % 2 == 0)//函数指针能被2整除时,指向m,不能被2整除时,指向n{abc = m;}else{abc = n;}l = abc(i+1);//执行函数指针,将返回值赋值给指针l。参数是i+1cout << i+1 << "#:" << endl;//第(i+1)#cout << "l = " << l << endl << " *l = " << *l << endl << endl;//打印l(指针)和l指向的地址的值}system("pause");return 0;}int *m(int a){int *x = new int;//new一个int地址出来*x = a;//新地址的值是参数areturn x;//返回地址}int *n(int a){int *x = new int;//new一个int地址出来*x = a*2;//新地址的值是参数a*2return x;//返回地址}
输出:
1#:l = 00740A10 *l = 12#:l = 00740A40 *l = 43#:l = 00740670 *l = 34#:l = 00740CA0 *l = 85#:l = 00740CD0 *l = 5请按任意键继续. . .
解释:
①当参数i(根据for循环而变)能否被2整除,让函数指针指向不同的函数;
声明一个函数指针数组:
除了上面声明一个函数指针,利用if语句使得指针指向不同函数,也可以声明一个函数指针数组,然后修改数组的成员标号,来让其指向不同的函数。
例如:
函数类型* (*函数名[函数指针成员个数]) (参数类型) ={ 函数1, 函数2};
可以将上面的代码
int*(*abc)(int);//创建一个函数指针,面对对象是返回值是指针的函数int *l;//创建一个指针,将函数的返回值(指针)赋值给他。for (int i = 0;i < 5;i++){if (i % 2 == 0)//函数指针能被2整除时,指向m,不能被2整除时,指向n{abc = m;}else{abc = n;}l = abc(i+1);//执行函数指针,将返回值赋值给指针l。参数是i+1……以下略……
改为:
int*(*abc[2])(int) = { m,n };//创建一个 函数指针 数组,成员分别指向不同的函数int *l;//创建一个指针,将函数的返回值(指针)赋值给他。for (int i = 0;i < 5;i++){l = abc[i%2](i+1);//利用i/2求余,调用不同的函数指针数组成员,并将返回值赋给指针l……以下略……
输出结果是相同的,但减少了代码量。
原理:
①不同函数指针数组成员指向不同函数;
②然后利用成员编号(abc[i])的不同,通过i%2的结果,得到不同的函数指针成员,再指向不同的函数(m或者n)。
③使用函数指针数组,就像使用普通的指针数组那样,把“[ ]”加在函数名后即可。
④关于auto:auto的效果是根据赋值来源的类型,自动为变量提供类型推断。
例如int a=4; auto b=a; 则b也是int类型。
auto只能用于单值类型初始化,比如在上面那个代码中,加入:auto b=m;
则相当于 int*(*b)(int)=m; 这样
又因为auto只能单值,因此不能auto b={m,n}; 这样,在后面的代码是不能b[0]调用m,b[1]调用n的。
但是,可以:
int*(*abc[2])(int) = { m,n };//创建一个 函数指针 数组,成员分别指向不同的函数
auto b = abc;
这样,在后面的代码,可以用b[i]代替abc[i]了;
函数指针的地址:
指针赋值:int a; int *b=&a; 这样的格式
重要:函数的名字是函数,字符串、数组的名字也只是字符串和数组;
只不过,在大多数情况下(例如不加地址运算符&),他被转化为指向他本身的指针。
如代码:
#include<iostream>using namespace std;int* a1(void);int* a2(void);int* a3(void);int main(){int*(*b1)(void);//b1是一个函数指针,他可以指向a1~a3中的某一个函数b1 = a1;//指针b1指向函数a1,则b1输出的是函数a1的地址,//b1()是调用函数b1,然后得到其返回值(int指针)。//*b1是b1指针指向的值(依然是函数a1,和b1等价)//(*b1)()和b1()是等价的。都是函数a1的返回值(这里是一个指针)//*b1()和*(*b1)()是等价的,都是返回值(是个指针)指向地址的值auto b2 = a2;cout << b1() << ": " << *b1() << endl;cout << (*b2)() << ": " << *(*b2)() << endl;int*(*c[3])(void) = { a1,a2,a3 };//创建一个函数指针数组,分别指向a1,a2,a3//运算符[]的优先级高于*,因此先是一个数组,然后才是指针,所以是一个指针数组。而这个指针数组又是指向函数的,所以是函数指针数组auto d = c;//c是一个函数指针数组,auto d=c,于是d也是一个同样类型的数组(函数指针数组,且包含3个成员)int *x_0 = c[0]();//c[0]是函数指针数组的第一个成员,他指向函数a1,因此c[0]后面加上()实际上就是调用a1。函数a1的返回值是一个int指针,所以被赋值给x_0这个指针int *x_1 = (*d[1])();//d[1]指向a2,加上*,函数a2被转化为一个指针,*d[1]就是*(a2),a2被转化为一个指向自己的函数,于是值还是自己。于是(*d[1])相当于a2,然后加上()就相当于调用函数a2//然后a2函数的返回值(int类型指针)被赋值给x_1int y_0 = *c[0]();//由于()优先级比*高,因此是先调用函数a1,得到返回值(指针),然后解除指针运算,得到的是返回值指向的地址int y_1 = *(*d[1])();//同上auto m = &c;//m是一个指向(函数指针数组)的指针。//与上面的d不同,上面的d指针数组被赋值给d,因此d也是一个指针数组。//这里的m是指针数组的地址被赋值给m,因此m是一个指针(因为地址),他指向的是c这个指针数组。//因此m(一个指针,指向指针数组的地址)和*m(指针的值——指针数组的地址——指针数组第一个成员的地址),c指针数组(通常被转化为指向这个指针数组的指针)//&m表示为m指针的地址,&(*m)表示指针数组的地址,&c表示指针数组的地址(同第一个成员的地址),&c[0]指针数组第一个成员的地址cout << &m << endl;//m指针的地址cout << &(*m) << endl;//指针数组的地址cout << &c << endl;//指针数组的地址cout << &c[0] << endl;//指针数组第一个成员的地址,同上面cout << endl;cout << m << endl;//指针(表示为指针指向的地址——指针数组的地址)cout << *m << endl;//指针的值(指向地址的值——是函数指针数组,又通常被转化为指向他本身的指针,因此同m)cout << c << endl;//指针数组,通常被转化为指向他的指针。c[0]同ccout << endl;int*(*(*n)[3])(void) = &c;//n的类型同m//首先把(*n)看做一个整体,*(*n)[3]中,(*n)这个整体是一个指针数组(包含3个元素的),而n是一个指针,这个指针指向这个整体的指针数组。//注意对比:int*(*c[3])(void) = { a1,a2,a3 }; //因为n这个指针指向整体的这个指针数组,所以他的值是&c,c是这个指针数组整体的地址//因为n指向c这个指针数组,所以*n就是指针数组(因为n指向它),因此(*n)[1]就是这个指针数组的第二个成员c[1](指向函数a2)cout << (*n)[1] << "和" << a2 << endl;system("pause");return 0;}int* a1(void){static int a = 1;int*m = &a;return m;}int* a2(void){static int a = 2;int*m = &a;return m;}int* a3(void){static int a = 3;int*m = &a;return m;}
输出:
0087B034: 10087B038: 20039FE380039FE800039FE800039FE800039FE800039FE800039FE80008713F2和008713F2请按任意键继续. . .
解释:
①正如代码前面红字绿字所说,无论是函数、字符串、数组或者其他什么的,他们的名字,并不是地址,只是大多数情况下,被转化为指向他自己的指针。因此,假如函数名字是abc,那么abc、*abc(指向自己的指针的值,还是自己),**abc等,都是函数的地址(因为被转化为指向他自己的指针了)。
②&是显示他的地址。例如&函数名,显示的是函数名的地址,因此,函数a1和函数&a1(代码中没有展现),他们的值是相同的,只不过a1是通过指向他的指针(a1的地址)所展现,而&a1就是输出函数a1的地址。
③指针数组的声明方式是
int*指针数组名[成员个数] = {第一个成员指向的地址, 第二个成员指向的地址, ……}
而
int *指针名=数组名;
是一个指向数组的指针——他解除运算后,就不是指针了。
举个简单例子,如代码:
int a[3] = { 1,2,3 };
int*b[3] = { &a[0],&a[1],&a[2] };
int*c = a;
cout << *c[0] << endl; //编译器会提示c[0]不是指针
cout << *b[0] << endl;//但这个b[0]就没问题
c[0]能显示a[0]值的原因在于,c是一个int类型的指针,他指向a[3]这个数组,相当于指向a[3]这个数组的第一个成员,因此c输出的是数组第一个成员的地址。
而c[0]则输出的是这个地址的值([0]有点类似*c),c[1]则是输出这个地址往右偏移一个int距离的地址的值。同理,b[0][0]和*b[0]展现出来的值是一样的。
是不是这个原因不确定,但是结果根据观测,是确定的。
这个代码,其中指针数组b[0],b[1],b[2]分别指向不同的数组a的成员的地址,但不代表其是连续的,即b[2]-b[1]不一定等于b[1]-b[0],因为是三个指针分别赋值(指向不同的地址)。
而c[0],c[1]是连续的,因为相当于偏移了一个int的内存地址距离。因此c[3]指向的是a[2]后面一个int距离的内存地址(输出的是这个地址的值),而b[3]由于没有声明和初始化,所以b[3]是不确定的(并不一定在b[2]后面一个int距离的内存地址)。
使用typedef进行简化:
之前有说过,typedef可以用一个别名替代原名:
例如: typedef 原名 别名;
在某些情况下,可以极大的简化输入。
例如上面,我们要表示一个函数指针,需要例如:
int *(*函数指针)(void); 这样
如果使用typedef,则可以简化,如:
typedef int*(*abc)(void);
这个时候,abc就表示是函数指针,例如abc x_1,表示,x_1是一个函数指针。
如代码:
#include<iostream>using namespace std;typedef int *(*abc)(void);//使用abc,让abc表示函数指针//另,好像不能用typedef的别名来代替函数原型和函数头int* a_1(void);//a_1函数int main(){abc m1 = a_1;//m1指向函数a_1cout << *m1() << endl;//m1()调用函数a_1(得到返回值,是一个指针),加*则解除指针,输出值system("pause");return 0;}int* a_1(void){int a = 10;int *b = &a;return b;}
输出:
10请按任意键继续. . .
总结:
①个人推测:假如使用typedef,将例如abc之类的代码,放在原本应该是这个类型的名字(函数名、字符串名、变量名等)之类的位置。那么这个例如是abc的代码,就是该类型的别名,
使用方法是:abc之类的代码 def之类名字
就是指def是abc所表示的类型。
如上面代码中,abc表示是函数指针的别名,则abc m1也是函数指针。
②指针数组也可以用typedef来使用别名。例如代码:
typedef int *(*abc[2])(void);//使用abc来作为此类指针数组的别名
abc m = { a_1,a_2 }; //m这个指针数组分别指向函数a_1和函数a_2
- (九十九)函数指针
- (九十九)桥接的介绍
- Android开发笔记(九十九)圆形转盘
- PyGobject(九十九)Cairo系列——环形加载图标
- 函数指针和指针函数(返回指针的函数)
- 指向函数的指针(函数指针)和指针函数
- 函数指针与指针函数(转载)
- C语言中的指针(指针数组 数组指针 函数指针 函数指针数组 指向函数指针数组的指针)
- 指针学习总结(函数指针,指针型函数)
- (未完)函数指针
- 函数指针(C++)
- 函数指针 (变量)
- 函数指针(转)
- 函数指针(1)
- 函数指针(整理)
- 函数指针 (1)
- swap函数(指针)
- (39)函数指针
- 平方之哈希表
- 算法设计题2.19~2.20-线性表-第2章-《数据结构习题集》-严蔚敏吴伟民版
- OC学习小结之数据类型间转化
- extern "c"作用
- 获取文字框架大小的方法
- (九十九)函数指针
- TSP问题
- ruby on rails模拟HTTP请求发生错误:end of file reached
- LeetCode 283 Move Zeroes(移动所有的零元素)
- 实战c++中的智能指针unique_ptr系列-- 使用unique_ptr来避免if多层嵌套
- unity shader的固定管线(fixed function shader)
- Android自定义控件之仿知乎详情页
- spring注入properties属性配置
- InfoQ中文站特供稿件:Rust编程语言的核心部件