回调函数与简单的例子

摘自:1、 http://blog.sina.com.cn/s/blog_5061633601009lmb.html

回调函数:

         回调函数是用户由用户实现的、注册到低层API当中的、当满足一定条件时会触发该回调函数按照用户的意图运行。回调函数的用途很广泛,例如:定时器事件回调处理、泛型编程中用户定义的算子等等,都可以看作是利用函数指针实现回调用户定义的函数。

简单的例子1:

我们实现一个如下这样一个例子,循环输入[1,100]到Center()函数中,当Center()发现当前数能被10整除,回调FCallBack()进行相应的处理。
[cpp]  view plain copy
  1. #include <stdion.h>  
  2.   
  3. typedef void (*CallBack) (int);  
  4.   
  5. void FCallBack(int i)  
  6. {  
  7.     printf("my is %d\n", i);  
  8. }  
  9.   
  10. void Center(int i, CallBack f)  
  11. {  
  12.     if (i %10 == 0)  
  13.     {  
  14.          f(i);  
  15.     }  
  16. }  
  17.   
  18. int main()  
  19. {  
  20.     for (int i = 1; i <= 100; i++)  
  21.     {  
  22.          Center(i, FCallBack);  
  23.     }  
  24.   
  25.      return 0;  
  26. }  

简单的例子2:


这里有一个简单的函数,它用于在单链表中查找一个值。它的参数是一个指向链表第 1 个节点的指针以及那个需要查找的值。

Node *
search_list(Node *node, int const value)
{
while(node!=NULL){
if( node->value == value )
break;
node = node->link;
}
return node;
}

这个函数看上去相当简单,但它只适用于值为整数的链表。如果你需要在一个字符串链表中查找,你不得不另外编写一个函数。这个函数和上面那个函数的绝大部分代码相同,只是第 2 个参数的类型以及节点值的比较方法不同。

一种更为通用的方法是使查找函数与类型无关,这样它就能用于任何类型的值的链表。我们必须对函数的两个方面进行修改,使它与类型无关。

首先,我们必须改变比较的执行方式,这样函数就可以对任何类型的值进行比较。这个目标听上去好像不可能,如果你编写语句用于比较整型值,它怎么还可能用于其它类型如字符串的比较呢?解决方案就是使用函数指针。调用者编写一个比较函数,用于比较两个值,然后把一个指向此函数的指针作为参数传递给查找函数。而后查找函数来执行比较。使用这种方法,任何类型的值都可以进行比较。

我们必须修改的第 2 个方面是向比较函数传递一个指向值的指针而不是值本身。比较函数有一个 void* 形参,用于接收这个参数。然后指向这个值的指针便传递给比较函数。(这个修改使字符串和数组对象也可以被使用。字符串和数组无法作为参数传递给函数,但指向它们的指针却可以。)

使用这种技巧的函数被称为回调数(callbackfunction),因为用户把一个函数指针作为参数传递其它函数,后者将”回调“用户的函数。任何时候,如果你所编写的函数必须能够在不同的时刻执行不同类型的工作或者执行只能由函数调用者定义的工作,你都可以使用这个技巧。

【提示】
在使用比较函数的指针之前,它们必须被强制转换为正确的类型。因为强制类型转换能够躲开一般的类型检查,所以你在使用时必须格外小心,确保函数参数类型是正确的。

在这个例子里,回调函数比较两个值。查找函数向比较函数传递两个指向需要进行比较的值的指针,并检查比较函数的返回值。例如:零表示相等的值,现在查找函数就与类型无关,因为它本身并不执行实际的比较。确实,调用者必须编写必需的比较函数,但这样做是很容易的,因为调用者知道链表中所包含的值的类型。如果使用几个分别包含不同类型值的链表,为每种类型编写一个比较函数就允许单个查找函数作用于所有类型的链表。

程序段01 是类型无关的查找函数的一种实现方法。注意函数的第 3 个参数是一个函数指针。这个参数用一个完整的原型进行声明。同时注意虽然函数绝不会修改参数 node 所指向的任何节点,但 node 并未被声明为 const 。如果 node 被声明为 const,函数将不得不返回一个const结果,这将限制调用程序,它便无法修改查找函数所找到的节点。


#include <stdio.h>
#include "node.h"

Node *
search_list( Node *node, void const *value,int (*compare)( void const *, void const *) )
{
while (node!=NULL){
if(compare(&node->value, value)==0)
break;
node=node->link;
}
return node;
}

指向值参数的指针和 &node->value 被传递给比较函数。后者是我们当前所检查的节点值。

在一个特定的链表中进行查找时,用户需要编写一个适当的比较函数,并把指向该函数的指针和指向需要查找的值的指针传递给查找函数下面是一个比较函数,它用于在一个整数链表中进行查找。
int
compare_ints( void const *a, void const *b )
{
if( *(int *)a == *(int *)b )
return 0;
else
return 1;
}

这个函数像下面这样使用:

desired_node = search_list ( root, &desired_value, compare_ints );

注意强制类型转换:比较函数的参数必须声明为 void * 以匹配查找函数的原型,然后它们再强制转换为 int * 类型,用于比较整型值。

如果你希望在一个字符串链表中进行查找,下面的代码可以完成这项任务:

#include<string.h>
...
desired_node = search_list( root, "desired_value", strcmp);

碰巧,库函数 strcmp 所执行的比较和我们需要的完全一样,不过有些编译器会发出警告信息,因为它的参数被声明为 char * 而不是
void *。

(二)转移表
转换表最好用个例子来解释。下面的代码段取自一个程序,它用于实现一个袖珍式计算器。程序的其他部分已经读入两个数(op1和op2)和一个操作数(oper)。下面的代码对操作符进行测试,然后决定调用哪个函数。

switch( oper ){
case ADD:
result = add( op1, op2);
break;
case SUB:
result = sub( op1, op2);
break;
case MUL:
result = mul( op1, op2);
break;
case DIV:
result = div( op1, op2);
break;

......

对于一个新奇的具有上百个操作符的计算器,这条switch语句将非常长。

为什么要调用函数来执行这些操作呢?把具体操作和选择操作的代码分开是一种良好的设计方法,更为复杂的操作将肯定以独立的函数来实现,因为它们的长度可能很长。但即使是简单的操作也可能具有副作用,例如保存一个常量值用于以后的操作。

为了使用 switch 语句,表示操作符的代码必须是整数。如果它们是从零开始连续的整数,我们可以使用转换表来实现相同的任务。转换表就是一个函数指针数组。

创建一个转换表需要两个步骤。首先,声明并初始化一个函数指针数组。唯一需要留心之处就是确保这些函数的原型出现在这个数组的声明之前。

double add (double,double);
double sub (double,double);
double mul (double,double);
double div (double,double);
......
double ( *oper_func[] )( double, double)={
add,sub,mul,div,...
};

初始化列表中各个函数名的正确顺序取决于程序中用于表示每个操作符的整型代码。这个例子假定ADD是0 ,SUB是1,MUL是2,依次类推。

第 2 个步骤是用下面这条语句替换前面整条 switch 语句!
result = oper_func[ oper ]( op1,op2 );
oper从数组中选择正确的函数指针,而函数调用操作符执行这个函数。



以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值