网络编程基础(1)

本文详细介绍了函数指针的概念及用途,包括作为参数传递给其他函数、用于散转程序和实现C语言中的面向对象封装。并通过实例展示了如何定义和使用函数指针,最后深入解释了回调函数的作用和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1 回调函数(CallBack Function)

 

    所谓回调函数,本质就是函数指针。所以先要搞清楚函数指针的问题。

 

1.1.1 函数指针

    一个函数占用一段连续内存。当掉用一个函数,实际上是跳转到函数入口地址,执行函数体,完成后返回。函数的入口地址是由函数名来标记的。
    函数指针是一种特殊类型的指针,它指向一个函数的入口地址。
    声明函数指针的语法形式为: 
   类型 (*函数指针名)(形参列表)
    例如:
    定义指向函数int func1(int arg11,char arg12)与int func2(char arg)的函数指针
    int (*pfunc1)(int ,char)
    pfunc1=func1;//将函数func1的地址赋给函数指针pfunc1
    int (*pfunc2)(int)
    pfunc2=func2;
    从函数指针的定义可以看出,函数指针的类型实际上是由函数签名决定的。函数签名包括函数名、函数形参类型的有序列表和函数返回值类型。
   一个函数指针的定义规定了它只能指向特定类型的函数。如果两个函数的形参列表和返回值类型相同,只有函数名和函数体不同,则可以使用相同类型的函数指针。
    函数指针的使用:
int (*fp)(int a);
fp=test;//将函数test的地址赋给函数学指针fp
cout<<fp(5)<<"|"<<(*fp)(10)<<endl;
   //上面的输出fp(5),这是标准c++的写法,(*fp)(10)这是兼容c语言的标准写法,两种同意,但注意区分,避免写的程序产生移植性问题!
    实际用途:
    实际上还有一种特殊的或说通用的函数指针,在定义这类函数指针时,只需要指定函数返回值类型,而留空形参列表,这样就可以指向返回值类型相同的所有函数。例如:
int (*pfunc)();
    这样定义的pfunc就可以指向前面提到的func1和func2,因为他们都返回整型值。
   经常见到使用typedef来定义函数指针的类型,typedef 方法可以有效的减少括号的数量,以及理清层次,所以受到推荐。其方法如下
    typedef void(* FP)(char);//这里不是声明函数指针,而是定一个函数指针的类型
    FP func3; //用上面定义的函数指针类型定义了func3的函数指针


    函数指针最常见的三个用途是:
    ①作为参数传递给其他函数。
    这样可以把多个函数用一个函数体封装起来,得到一个具有多个函数功能的新函数,根据传递的函数指针变量值的不同,执行不同的函数功能。这是函数嵌套调用难以实现的。参数的传递可以由程序员设定,也可以由用户输入读取,因此具有较大的灵活性和交互性。
    另外还可以用于回调函数。使用void配合,还可以将对不同数据类型的数据进行相同处理的多个函数封装为一个函数,增强函数的生命力。
    函数指针、回调函数屏蔽了函数的差异性。具体理解。
    ②用于散转程序。
    这种程序首先建立一个函数表(实际上是一个函数指针数组),表中存放了各个函数的入口地址(或函数名),根据条件的设定来查表选择执行相应的函数。这样也可以将多个函数封装为一个函数或者程序,散转分支条件可以由程序员设定,也可以由用户输入读取,甚至是外设的某种特定状态(这种状态可以是不受人为控制的)。
    ③实现C的面向对象的类的封装。
    C语言中的struct与C++中的class有很大不同,除了缺省的成员属性外(struct的成员缺省为public的,可随意使用,而class成员缺省为private的),struct还很难实现类成员函数的封装。struct的成员一般都是数据成员,而非函数成员。因此,为了在C语言中,为某个struct定义一套自己的函数对结构数据成员进行操作,可以在struct结构体中增加函数指针变量成员,在初始化时使它指向特定函数即可。
应用举例:
    1、假设定义了四个函数:add(int, int)、sub(int, int)、mul(int, int)、div(int, int),可以将其封装为一个四则运算计算器函数: 

1  double calculator(int x,int y,int (*pfunc)(intint)) {
2     double result;
3     result = pfunc(x,y);
4     return result;
5 }

    又例如,在一个链表查询程序中,要通过比较节点的特征值来查询节点,不同类型的数据的比较方式不一样,整型等可以直接比较,字符串却要用专门的字符串操作函数,为了使代码可重用性更高,可以使用一个比较函数来代替各种不同数据类型的直接比较代码,同时,比较函数也必然是数据类型相关的,因此要使用void指针和函数指针来转换为类型无关的比较函数,根据相应的数据类型,调用相应的函数(传递相应的函数指针)。一个实例是:
    int (*compare)(void const *,void const *);
    这个函数指针可以接受任意类型的数据的指针参数,同时返回int值作为比较结果标志。一个比较整型数据的比较函数是:  

int compare_ints(void const *a, void const *b) {
    
if*(int *)a == *(int *)b )
    
return 0;
    
else
    
return 1;
}

    2、散转程序。通过一个转移表(函数指针数组)来实现。还是上面定义的四个四则运算函数,可以建立这样一个转移表(注意初始化该转移表的语句前面应有add等相应函数原型声明或定义):
double (*calculator[])(int, int) = {
    add, sub, mul, div
};
    这样,calculator[0] == add,calculator[1] == sub,...
    使用result = calculator[oper](x, y);就可以代替下面整个switch语句:

 switch( oper ) {
   case 0: result = add(x, y); break;
   case 1: result = sub(x, y); break;
   ...
}

    3、C的面向对象化。一个对象包括数据和对数据的操作。C语言中的struct只有数据成员,因此要增加一些“伪数据成员”即函数指针来实现对数据的操作。例如:


ContractedBlock.gifExpandedBlockStart.gifCode
#ifndef C_Class
#define C_Class struct
#endif
C_Class student{
    C_Class student 
*student_this;
    
char name;
    
int height;
    
int gender;
    
int classnum;
    
    
void (*Oper)( C_Class student *student_this );
    

   1.1.2 回调函数

    简言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
    为什么要使用回调函数?
    因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。
    回调函数在实际中的作用。先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。
    例如,回调用于通知机制。有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer() API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。
   【函数指针与函数回调的方法屏蔽了函数实现的差异性。对主调函数而言,回调函数可否看做主调函数的一个需接口。主调函数使用回调函数,有时需要为回调函数提供参数,但从不关心回调函数由谁来实现以及实现的细节。】

转载于:https://www.cnblogs.com/qiyanfeng/articles/1434168.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值