(九十九)函数指针

来源:互联网 发布:macd指标通用源码 编辑:程序博客网 时间:2024/05/15 19:49

与数据项(比如intstringchar等)相似,函数也有地址。

 

函数的地址存储其机器语言代码的内存的开始地址(不懂)

 

这些地址对用户而言,没什么用,也不重要,但对程序而言,却很有用。

 

例如,可以编写将另外一个函数的地址作为参数的函数。这样,第一个函数将能够找到第二个函数,并运行它。

与直接调用第二个函数相比,这种方法很笨拙,但他允许在不同的时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数。

 

函数指针和指针函数的区别:

函数指针:指向某个函数的指针(即这个指针指向函数的地址);

指针函数:返回值是指针的函数

 

函数指针的基础知识:

函数的地址:

函数名 就是函数的地址(像字符串一样);

 

声明函数指针:

有些像函数返回指针,但是要把*和函数名用括号括起来。例如:函数类型 (*函数名) (参数); 

注意:①声明的函数指针,要和函数的特征标(例如类型、函数的参数类型)要相符;

*函数名 此时是函数,而 函数名 此时是指针。

 

函数指针作为参数时:

函数类型 函数名(函数类型 (*函数名)(函数指针的参数类型) );

注:蓝色部分为函数指针作为参数时的写法。

 

调用函数指针时:

和声明函数指针类似,用函数指针的 (*函数名) 取代普通函数 函数名 即可。

如:(*函数名)(函数指针的参数)  这样。

也可以像使用普通函数那样,通过  函数名(参数)  这样调用函数,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)。

 

使用函数指针数组,就像使用普通的指针数组那样,把“[ ]”加在函数名后即可

 

④关于autoauto的效果是根据赋值来源的类型,自动为变量提供类型推断。

例如int a=4; auto b=a; b也是int类型。

auto只能用于单值类型初始化,比如在上面那个代码中,加入:auto b=m;

则相当于 int*(*b)(int)=m; 这样

又因为auto只能单值,因此不能auto b={m,n}; 这样,在后面的代码是不能b[0]调用mb[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之类名字

就是指defabc所表示的类型。

 

如上面代码中,abc表示是函数指针的别名,则abc m1也是函数指针。

 

②指针数组也可以用typedef来使用别名。例如代码:

typedef int *(*abc[2])(void);//使用abc来作为此类指针数组的别名

abc m = { a_1,a_2 }; //m这个指针数组分别指向函数a_1和函数a_2


0 0
原创粉丝点击