C/C++ 函数指针 总结
什么是函数指针
就像某一变量的地址可以存储在相应的指针变量中一样,指向函数的指针中保存着函数代码起始处的地址
函数指针的声明
当声明一个函数指针时,必须声明它指向的函数类型。要指定函数类型,就要指出函数的返回类型和参数列表,如:
void (* pf)(int, int); //pf是一个函数指针,它指向的函数类型为:返回值为void,参数列表为(int, int)
函数指针的赋值
函数名本身即代表函数的地址,因此给函数赋值时可以不加&符号,如:
void func(char *);
void (* pf)(char *);
pf = func; //可以
pf = &func; //可以
函数指针的使用
一般来说,函数指针有以下两个用处:
调用函数
此时函数指针可看作函数的别名,如下面三条语句等价:
func("abc");
pf("abc"); //可以
(* pf)("abc"); //可以
将函数作为其他函数的参数
这是函数指针最普遍的用法,如:
void show(void (* pf)(char *), char * str); //第一个参数为函数指针
注意:函数指针实参可以指向不同函数,但这些函数类型(返回类型和参数列表)必须一样
使用函数指针作为函数参数的典型例子有库函数qsort( )
在C++类中使用函数指针
typedef 返回类型(类名::*新类型)(参数表)
class CA { public: char lcFun(int a){ return; } }; CA ca; typedef char (CA::*PTRFUN)(int); PTRFUN pFun; int main() { pFun = CA::lcFun; ca.(*pFun)(2); }
在这里,指针的定义与使用都加上了“类限制”或“对象”,用来指明指针指向的函数是那个类的这里的类对象也可以是使用new得到的。比如:
CA *pca = new CA;
pca->(*pFun)(2);
delete pca;
而且这个类对象指针可以是类内部成员变量,你甚至可以使用this指针。比如:
类CA有成员变量PTRFUN m_pfun;
void CA::lcFun2()
{
(this->*m_pFun)(2);
}
一句话,使用类成员函数指针必须有“->*”或“.*”的调用。
函数指针的应用
标准模板库中的sort排序
template<class RandomAccessIterator, class Predicate>
void sort(RandomAccessIterator first, RandomAccessIterator last, Predicate comp);
sort允许使用自己定义的比较函数(第三个参数)
// 比较分数
bool compareScore(const FightAttr& a, const FightAttr& b)
{
return a.score< b.score;
}
用指向函数的指针作函数参数
在C语言中,函数指针变量常见的用途之一是作为函数的参数,将函数名传给其他函数的形参。这样就可以在调用一个函数的过程中根据给定的不同实参调用不同的函数。
例如,利用这种方法可以编写一个求定积分的通用函数,用它分别求5个函数的定积分:每次需要求定积分的函数是不一样的。可以编写一个求定积分的通用函数integral,它有3个形参:
下限a上限b,以及指向函数的指针变量fun函数原型可写为
double integral (double a,double b,double (*fun)(double));
分别编写5个函数f1,f2,f3,f4,f5, 用来求上面5个函数的值。然后先后调用integral函数5次,每次调用时把a,b以及f1,f2,f3,f4,f5之一作为实参,即把上限、下限以及有关函数的入口地址传送给形参fun,在执行integral函数过程中求出各函数定积分的值。
函数指针与指针函数的区别
1、函数指针
函数指针其实就是指向一个函数地址的指针,我们知道数值和函数一样都有自己的存在空间,也都有程序的首地址,数组名表示着整个数组代码占用内存的首地址,同理函数名也就是整个函数代码占用内存的首地址,函数指针就是指向函数占用内存的首地址,其定义格式如下:
type (*name)(参数列表);//函数返回值 (*函数指针)(参数列表)
例如我们可以定义一个函数返回值为int型的,参数列表为(int x)的函数,然后定义一个函数指针来指向他。
int func(int x);//定义函数
int (*f)(int x);//定义函数指针
f=func;//这样函数指针*f就指向函数func了
需要注意的是:函数指针的返回值和参数列表要和指向的函数的必须完全一样,这样才可以对其进行赋值。
2、指针函数
一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。
返回指针的函数,一般定义格式为:
类型标识符 *函数名(参数表)
int *f(x,y);
其中x,y是形式参数,f是函数名,调用后返回一个指向整型数据的地址指针。f(x,y)是函数,其值是指针。
如:char *ch();表示的就是一个返回字符型指针的函数。
函数指针与typedef
1、定义函数指针类型
// 定义一个原型为int Fun( int a );的函数指针
typedef int (*PTRFUN) ( int aPara );
2、函数指针变量的定义
PTRFUN pFun; // pFun 为函数指针变量名
int (*pFun2) ( int a ); // pFun2也是函数指针变量名
函数指针与动态绑定
// 获得函数指针的大小
unsigned psize = sizeof (void (*) ());
// 为函数指针声明类型定义
typedef void (*pfv) ();
pfv是一个函数指针,它指向的函数没有输入参数,返回类行为void。使用这个类型定义名可以隐藏复杂的函数指针语法。
指针变量应该有一个变量名:
void (*p) (); //p是指向某函数的指针
p是指向某函数的指针,该函数无输入参数,返回值的类型为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值,值的内容是署名匹配的函数名和返回类型。例如:
void func()
{
/* do something */
}
p = func;
p的赋值可以不同,但一定要是函数的地址,并且署名和返回类型相同。
传递回调函数的地址给调用者
现在可以将p传递给另一个函数(调用者)- caller(),它将调用p指向的函数,而此函数名是未知的:
void caller(void(*ptr)())
{
ptr(); /* 调用ptr指向的函数 */
}
void func();
int main()
{
p = func;
caller(p); /* 传递函数地址到调用者 */
}
如果赋了不同的值给p(不同函数地址),那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。
其他
1.总之,函数指针赋值时,&可加可不加;函数指针取值时(即作为函数别名进行调用时),*可加可不加
2.要注意带有返回值的函数,如:
func1(sqrt); //将函数地址作为参数传递
func2(sqrt(4.0)); //将函数返回值作为参数传递
区别在于函数名后跟不跟参数列表
example:
void sort(int a[], int n); //sort函数 void (*p)(int a[], int n) = NULL; //p就是指向void (int a[], int n)类型的函数指针 p = sort; //函数名就是函数地址 int arry[3] = {1, 2, 3}; sort(arry, 3); p(arry, 3); //可以直接调用