1:函数名为指针
首先,在C语言中函数是一种function-to-pointer的方式,即对于一个函数,会将其自动转换成指针的类型.如:
1 #include
2
3 void fun()
4 {
5 }
6
7 int main()
8 {
9 printf("%p %p %p\n", &fun, fun, *fun);
10 return 0;
11 }
这三个值的结果是一样的. 其实对于最后的那个*fun, 即使前面加上很多个*号, 其结果也不变, 即**fun, ***fun的结果都是一样的. 对于这个问题, 因为之前讲过函数是一种function-to-pointer方式, 其会自动转换成指针的类型, &fun是该函数的地址, 为指针类型, fun是一个函数, 会转换成其指针类型, 而对于*fun, 由于fun已经变成了指针类型, 指向这个函数, 所以*fun就是取这个地址的函数, 而又根据function-to-pointer, 该函数也转变成了一个指针, 所以以此类推, 这三个值的结果是相同的.
2:回调函数
通过将回调函数的地址传给调用者从而实现动态调用不同的函数。因此当我们想通过一个统一接口实现不同的内容,这时用回掉函数非常合适。
若要实现回调函数,最关键的是要把调用函数的参数定义为函数指针类型。函数指针的定义这里稍
微提一下。比如:
int (*ptr)(void); 这里ptr是一个函数指针,其中(*ptr)的括号不能省略,因为括号的优先级高于星号,那样就成了一个返回类型为整型的函数声明了。int为返回类型,括号内为函数的参数。
下面通过一个例子来解释回调函数的用法:
1 #include
2 #include
3 int Test1(int num) 4 { 5 printf("i am test1,the data is %d \n",num); 6 return 0; 7 } 8 int Test2(int num) 9 { 10 printf("i am test2,the data is %d\n",num); 11 return 0; 12 } 13 14 int Caller(int (*ptr)(int n),int n)//指向函数的指针作函数参数,这里第二个参数是函数指针的参数 15 { //不能写成void Caller2(int (*ptr)(int n)),这样的定义语法错误。 16 int a=(*ptr)(n); 17 return a; 18 } 19 int main() 20 { 21 22 Caller(Test1,20); 23 printf("************************\n"); 24 Caller(Test2,10); 25 26 return 0; 27 }
下面介绍几种比较容易混淆的指针概念:
1:函数指针
1:函数指针的定义方式:
返回值类型 (* 指针变量名)(形参列表);
返回值为指针的函数定义: 返回指针类型 * 函数名(形参列表);
2:函数指针的赋值:
在赋值时,可以直接将函数指针指向函数名(函数名即代表该段代码的首地址),但是前提是:函数指针和它指向的函数的参数个数以及类型必须一致。函数指针的返回值类型与函数的返回值类型必须一致。
3:通过函数指针调用函数:
加上指针f指向函数func。(*f ) 和 func代表同一函数。
使用方法如下:
声明函数指针:int (*f)(int x);
函数指针赋值: f=func ( int func(int x));
函数指针调用函数: (*f)(x) (x为整型变量)
2:函数指针数组
函数指针数组是一个其元素是函数指针的数组。即,此数据结构是是一个数组,且其元素是一个指向函数入口地址的指针。
定义方式: 返回值 ( *数组名[个数]) (参数列表)
3:指向数组的指针
类型 (*变量名)[元素个数]
4: 指针数组
类型 *变量名[元素个数]
因为[] 比*具有更好的优先级。所以如果是变量a先和*结合则表示其为一个指针,如果a先和[]结合,则表示是一个数组。
带参数的回调函数:
//定义带参回调函数
void PrintfText(char* s)
{
printf(s);
}
//定义实现带参回调函数的"调用函数"
void CallPrintfText(void (*callfuct)(char*),char* s)
{
callfuct(s);
}
//在main函数中实现带参的函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText,"Hello World!\n");
return 0;
}
c++回调机制:
非静态成员函数作回调函数
当然如果是静态成员函数就好办跟全局函数是类似,到此为止世界还没有变乱,如在VC编程中用AfxBeginThread开启一个线程,就经常将参数AFX_THREADPROC pfnThreadProc定义为一个全局函数或静态成员函数,可是这两个都不方便访问类的非静态成员,之所以郑重其事地写这篇文章,就是以前静态回调用起来非常不爽。
回调函数是非静态成员函数呢?我们可不能简单地设为这样:
class CCallback
{
public:
void Func(int a)
{
cout<<"member function callback called with para="<
}
};
typedef void (CCallback::*pMemberFunc)(int);
void Caller(pMemberFunc p)
{
(*p)(1);
}
这样编译就不会通过的,因为非静态的成员函数必须通过对象来访问,好,我们稍稍改进一下:
class CCallback
{
public:
void Func(int a)
{
cout<<"member function callback called with para="<
}
};
typedef void (CCallback::*pMemberFunc)(int);
void Caller(CCallback* pObj,pMemberFunc p)
{
(pObj->*p)(1);
}
int main(int argc, char* argv[])
{
CCallback obj;
Caller(&obj,&CCallback::Func);
}
即给Caller多传个对象进去,好吧,貌似问题解决了,可是,调用者(如库的提供商)只知道回调函数接口长这样而已,事先全然不知客户的类是如何定义,终于模板登上场了:
template
void Caller(T* pObj,void (T::*p)(int))
{
(pObj->*p)(1);
}
其他不变的,把调用者这里换成模板就OK了,当然这个Caller也可以是成员函数,现在用这个方法写个小应用是没什么问题了,但是限制多多,如调用者一次只调用了一个实现,但现实情况往往是产生某个事件时,应该依次调用多个行为,即把挂在这个事件上的所有回调函数通通临幸一遍,还有回调是如此的重要,以至于C#不用库在语言本身层面就实现了它,我们也不可以到此草草了事,而是按照组件化的思维提供一套完善的回调机制,所谓完善,如上个例子中Caller只能接收一个参数为int,返回值为void的成员函数指针,等等,必须是这样的接口吗,想想参数为double行不行,如void (T::*p)(double)这样的函数传给它可以吗,int不是可自动转换为double吗,那这个函数指针也能自动转换吗,就像C#中的协变与逆变一样,不行,C+