松鼠的博客

导航

统计

声明函数指针并实现回调

声明函数指针并实现回调

作者:Danny Kalev

编译:赵湘宁

原文出处:Declaring Function Pointers and Implementing Callbacks 

 

--------------------------------------------------------------------------------

 

    程序员常常需要实现回调。本文将讨论函数指针的基本原则并说明如何使用函数指针实现回调。注意这里针对的是普通的函数,不包括完全依赖于不同语法和语义规则的类成员函数(类成员指针将在另文中讨论)。

 

声明函数指针

 

    回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。请看下面的例子:

 

void f()// 函数原型

 

上面的语句声明了一个函数,没有输入参数并返回void。那么函数指针的声明方法如下:

 

void (*) ();

 

    让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:

 

// 获得函数指针的大小

unsigned psize = sizeof (void (*) ());

 

/ 为函数指针声明类型定义

这一步在动态链接库中定义typedef void (*pfv) ();

 

pfv是一个函数指针,它指向的函数没有输入参数,返回类行为void。使用这个类型定义名可以隐藏复杂的函数指针语法

 

指针变量应该有一个变量名:

 

void (*p) (); //p是指向某函数的指针

 

    p是指向某函数的指针,该函数无输入参数,返回值的类型为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值,值的内容是署名匹配的函数名返回类型。例如:

void func()
{
    
/* do something */
}

= func;

 

p的赋值可以不同,但一定要是函数的地址,并且署名和返回类型相同。

 

传递回调函数的地址给调用者

 

    现在可以将p传递给另一个函数(调用者)- caller(),它将调用p指向的函数,而此函数名是未知的:

复制代码
void caller(void(*ptr)())
{
    ptr(); 
/* 调用ptr指向的函数 */
}

void func();

int main()
{
    p 
= func;
    caller(p); 
/* 传递函数地址到调用者 */
}
复制代码

 

    如果赋了不同的值给p(不同函数地址),那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。

 

调用规范

 

    到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。

 

    将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:

复制代码
//被调用函数是以int为参数,以int为返回值
__stdcall int callee(int);

// 调用函数以函数指针为参数
void caller( __cdecl int(*ptr)(int));

//在p中企图存储被调用函数地址的非法操作

__cdecl 
int(*p)(int= callee; // 出错
复制代码

 

指针pcallee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列。

posted on   Xproer-松鼠  阅读(315)  评论(0编辑  收藏  举报

编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示