函数指针
获取函数的地址
函数名 就是 函数地址。
int sum();
不需要&sum来获取其地址,函数的地址就是 sum
声明一个函数指针
函数原型:
double * get_address(double * a, int b);
声明一个 指向该函数 的 函数指针 funcp,并赋值:
double * (*funcp)(double * a, int b);
funcp = get_address;
声明的时候,需要指定返回值和参数列表,(*funcp)中*代表了这是一个指针,括号是必不可少的,因为()的优先级大于*:
double * *funcp(double * a, int b);
上面声明的是一个函数 funcp,返回值类型是double **
返回指针的函数,指向函数的指针,就差一个括号
定义一个函数指针 funcp,指向该函数:
double * (*funcp)(double * a, int b) = get_address;
auto funcp1 = get_address;
C++11的自动类型推断,可以简化函数指针的声明
使用函数指针调用函数
使用2中的函数指针调用函数
double a[2] = {1.135,2.246};
get_address(a, 1);
(*funcp)(a, 1);
funcp(a, 1);
三次调用都执行了get_address函数,(*funcp) 中的*是取值的意思,既然funcp是函数指针,那么对funcp取值就是函数呗,所以编译器对函数调用的支持有两种方式:
- 通过函数名
- 通过对函数指针取值
但是这里有个陷阱:既然函数指针指向函数的入口地址,那么对函数指针取值,不应该是函数的第一条指令吗?
编译器的动作是很复杂的,所以我个人观点,编译器在处理函数调用时,将函数指针取值后的调用——(*funcp)(a, 1),进行了特殊处理,而不同于数据指针取值的的逻辑。
funcp(a, 1)也调用get_address函数是因为:C++允许向使用函数名一样使用函数指针
我支持这种观点:函数名是指向函数的指针,那么函数指针和函数名具有同等地位
为什么不用 funcp = &get_address,因为函数名本身就是指针
funcp = get_address,获取的就是指针的值,即函数的入口地址
funcp = *get_address, 获取的是get_address函数中第一条代码的值
将函数名当函数指针用
g++编译器支持下面的代码:
double a[2] = {1.135,2.246};
(*get_address)(a, 1);
所以函数指针和函数名在数值和功能上是一样的
但是,却不能在定义函数原型的时候使用函数指针,如编译器不支持这样定义函数:
double * get_address(double *a, int b)
{
return a;
}
//出错,return前缺少初级表达式
double * (*haha)(double *a, int b)
{
return a;
}
函数指针数组
进阶的用法。
声明一个函数指针数组:
double * (*pa[3])(double *a, int b) = {f1, f2, f3};
因为[]的优先级高于*,所以*pa[3]表示pa是包含三个指针的数组,指针类型由代码其他部分确定
因为pa是数组,每一个元素是函数指针,所以调用f1函数:
pa[0](a, 1);
(*pa[0])(a, 1);
获取返回的指针指向的值:
*pa[0](a, 1);
*(*pa[0])(a, 1);
指向函数指针数组
使得一个指针,指向函数指针数组:
double * (*(*pd)[3])(double *a, int b) = {f1, f2, f3};
auto pd1 = &pa;
(*pa)[3] 表示指向数组的指针,数组元素的类型由其他部分确定,在此为函数指针
自动类型推断只能用于单值初始化
调用f1函数:因为pd是指向数组的指针,则*pd为数组
(*pd)[0](a, 1);
(*(*pd)[0])(a, 1);
注意pa和&pa的差别:
- pa是数组名,在取值上,是数组第一个元素的地址
&pa是整个数组的地址
所以,在取值的大小上没有差别
差别在取值的方式:p[0] = *pa = **&pa
-
二者在运算上有差别,若内存32位地址(四字节,内存变化以字节为单位):
pa+1,是数组第二个元素的地址,即&pa[0]+4 或&pa[1]
而&pa+1,则是以数组大小为单位加一,则&pa[0] +12
typedef简化函数指针的使用
函数指针的使用很简单,声明很麻烦,解决办法:typedef
所以,要定义函数指针的时候可以这样简化代码:
typedef double * (*funcp)(double *a, int b);
funcp fp = get_address;
代码实践
#include <iostream>
double * get_address(double *a, int b)
{
return a;
}
//编译器不支持
// double * (*haha)(double *a, int b)
// {
// return a;
// }
int main(int argc, char * argv[])
{
using namespace std;
double a[2] = {1.135, 2.246};
cout <<"通过函数名调用get_address函数:" <<endl;
cout <<get_address(a, 1) <<":" <<*get_address(a, 1) <<endl;
double *(*funcp)(double *a, int b);
funcp = get_address;
cout <<"通过函数指针调用get_address函数:" <<endl;
cout <<(*funcp)(a, 1) <<":" <<*(*funcp)(a, 1) <<endl;
cout <<"将函数指针看作函数名调用get_address函数:" <<endl;
cout <<funcp(a, 1) <<":" <<*funcp(a, 1) <<endl;
cout <<"将函数名看作函数指针掉用get_address函数:" <<endl;
cout <<(*get_address)(a, 1) <<":" <<*(*get_address)(a, 1) <<endl;
double * (*pa[3])(double *a, int b) = {get_address, get_address, get_address};
cout <<"函数指针数组首元素:" <<endl;
cout <<pa[0] <<"(" <<get_address <<")" <<endl;
cout <<"使用函数指针数组元素调用get_address函数:" <<endl;
cout <<pa[0](a, 1) <<":" <<*pa[0](a, 1) << endl;
double * (*(*pd)[3])(double *a, int b) = &pa;
cout << "使用指向“函数指针数组”的指针 调用get_address函数" << endl;
cout << (*pd)[0](a, 1) << ":" << *(*pd)[0](a, 1) << endl;
auto pd1 = &pa;
cout << "pd1: " << pd1 << endl;
cout << "pd: " << pd << endl;
cout << "pa: " << pa << endl;
cout << "&pa[0]:" << &pa[0] << endl;
cout << "&pa: " << &pa << endl;
cout << "pa+1 " << pa+1 << endl;//64位地址,增加8字节
cout << "&pa+1 " << &pa+1 << endl;//增加24字节
return 1;
}