1.Nginx和Redis中两种回调函数写法

#include <stdio.h>//仿Nginx风格
//结构外声明函数指针类型
typedef void (*ngx_connection_handler_pt)(int c);
//仿redis风格
typedef void redisCommandProc(int c);
typedef struct
{
int a;
//结构内定义函数指针变量pshow
void (*pshow)(int);
//结构内定义函数指针变量
ngx_connection_handler_pt   handler;
redisCommandProc *proc;
}TMP;void func(TMP *tmp)
{tmp->handler(tmp->a);tmp->proc(tmp->a);if(tmp->a >10)//如果a>10,则执行回调函数。{tmp->pshow(tmp->a);}
}void show(int a)
{printf("a的值是%d\n",a);
}void main()
{TMP test;test.a = 11;test.pshow = show;test.handler = show;test.proc = show;func(&test);
}

函数名就是一个指针,如同于数组a[],a和&a其实都是一样的。当调用一个函数时,我们都是直接用函数名调用,或者说通过指针调用。

风格1--tpyedef自定义函数指针类型

#include <stdio.h>
typedef int (*fp_t)(char c);int f0(char c) {
printf("f0, c = %c\n", c);
return 0;
}int f1(char c) {
printf("f1, c = %c\n", c);
return 1;
}int main(){int ret;fp_t fp;//fp是一个指向一个函数类型(返回的是int,参数是char)的函数指针fp = f0;ret = fp('a');通过函数指针调用函数fp = f1;ret = fp('x');return 0;
}

风格2--typedef自定义函数类型

#include <stdio.h>
typedef int f_t(char c);int f0(char c) {
printf("f0, c = %c\n", c);
return 0;
}int f1(char c) {
printf("f1, c = %c\n", c);
return 1;
}int main()
{int ret;f_t *fp;//f_t是函数类型,所以fp是指向此函数类型的指针fp = f0;ret = fp('a');fp = f1;ret = fp('x');//函数指针调用此函数return 0;
}

《C语言程序设计:现代方法:第2版》

2.函数指针定义

函数指针及应用
我们先来看一下以下 的声明:
int f(int);
int  (*pf)(int)=&f;//&操作符可选;因为函数名被使用时总是由编译器把它转换为函数指针;

或者

pf=f;
int ans;
ans=f(25);
ans=(*pf)(25);
ans=pf(25);//间接访问操作并非必需,因为编译器需要的是一个函数指针;

使用举例:

#include <stdio.h>
int getA(int a)
{
return a+1;
}int getB(int b)
{
return b*2;
}int search(void const * a,void const * b,int(*compare)(void const *,void const *))
{
return compare(a,b);
}
int copmare_int(void const *a,void const *b)
{
if(*(int *)a==*(int *)b)
{
return 0;
}
else{
return 1;
}
}int (*pf)(int k);
int main(void)
{
int f=0;
printf( "please input>>>\n");
scanf("%d",&f);
if(f==1)
{
pf=getA;
}
else{
pf=getB;
}
int ff=pf(f);
printf( "ff =%d\n", ff);int f1=0;
int f2=0;
int x1=2;
int x2=2;
f1=search(&x1,&x2,copmare_int);
printf( "f1 =%d\n", f1);
} 

3.函数指针应用一--回调函数

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

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

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

一种更为通用的方法是查找函数与类型无关,这样它就能用于任何类型的值的链表,我们必须对函数的两个方面进行修改,使它与类型无关.首先我们必须改变比较的执行方式,这样函数就可以对任何类型的值进行比较.这个目标听上去好象不可能,如果你编写语句用于比较整型值,它怎么还可能用于其他类型如字符串的比较呢?解决方案就是使用函数指针,调用者编写一个函数,用于比较两个值,然后把一个指向这个函数的指针作为参数传递给查找函数.然后查找函数调用这个函数来执行值的比较,使用这种方法,任何类型的值都可以进行比较.我们必须修改的第二个方面是向函数传递一个指向值的指针而不是本身.函数由一个void *形参,用于接收这个参数,然后指向这个值的指针便传递给比较函数,这个修改使字符串和数组对象也可以被使用,字符串和数组无法作为参数传递给函数,但指向它们的指针可以.

使用这种技巧的函数叫"回调函数"(callback function);因为用户把一个函数指针作为参数传递给其他函数,后者将"回调"用户的函数.任何时候,如果你所编写的

函数必须能够在不同的时刻执行不同类型的工作或执行只能由函数调用者定义的工作,你都可以使用这个技巧.许多窗口系统使用回调函数连接多个动作,如拖拽鼠标和点击按钮来指定用户程序中的某个特定函数.我们无法在这个上下文环境中为回调函数编写一个准确的原型,因为我们并不知道进行比较的值的类型.事实上,我们需要查找函数能作用于任何类型的值,解决这个难题的方法是把参数类型声明为"void *",表示"一个指向未知类型的指针".

/***在一个单链表中查找一个指定值的函数,它的参数是一个指向链表第一个节点**的指针,一个指向我们需要查找的值的指针和一个函数指针,它所指向的函数**用于比较存储于此链表中的类型的值.*/#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所指向的任何节点,但node并未声明为const。如果node被声明为const,函数不得不返回一个const结果,这将限制调用程序,它便无法修改查找函数所找到的节点。

在一个特定的链表中进行查找时,用户需要编写一个适当的比较函数,并把指向该函数的指针和指向需要查找的值的指针传递给查找函数。例如,下面是一个比较函数,它用于在一个整数链表中进行查找。

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);

4.函数指针应用二--转移表(jump table)

转移表最好用个例子来解释。下面的代码段取自一个程序,它用于实现一个袖珍式计算器。程序的其他部分已经读入两个数(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,接下去以此类推。

第二个步骤是用下面这条语句替换前面整条switch语句!

result=oper_func[oper](op1,op2);

oper从数组中选择正确的函数指针,而函数调用操作符将执行这个函数。

5.复杂的函数指针拆解

void (*signal (int signo,void (*func) (int) )) (int)

这一大堆看起来很难,其实仔细分析下不算很难搞。

首先要明白一件事:这里都是从最基本的语法展开的。

那么这里最基本的语法就是函数的声明:返回值 函数名(参数)。
先将这一大堆给看成void (f)(int)也就是将*signal (int signo,void (*func) (int) )看成f,那么相对而言就比较好理解了。
那么signal就是函数名,而他的返回值为指向函数f的指针即(指向一个返回值为void参数为int的函数的指针)。
然后signal的参数为signo和一个返回值为void参数为int的函数指针。
然后那个f的返回值为void 参数为int这就不必多说了。

好了,关于void (*signal (int signo,void (*func) (int) )) (int)这个字面上的意思大抵就是这样。

其实是unix信号通信机制里面的。具体如下:
void (*signal(int signo, void (*func)(int)))(int);
typedef void Sigfunc(int);

Sigfunc *signal(int, Sigfunc *);

从这里结合上面的分析我们可以得出singal这个函数的返回值类型为void(*)(int)。

同时
#define    SIG_ERR        (void (*)(int))-1
#define    SIG_DFL        (void (*)(int))0
#define    SIG_IGN        (void (*)(int))1

这里以(void(*)())1表示将1强制性转换为返回值为void参数为int的函数指针。
不知道干嘛要这样,后来看到一个demo后明白了。
if (signal(SIGINT, sig_int) == SIG_ERR)这里用来捕捉各个信号。
由于signal的返回值类型为void(*)(int)所以为了区别各个信号所以只能将这些东西定义为这么复杂的声明了。

关于typedef参考: 深度分析typedef

函数指针--Nginx和Redis中两种回调函数写法相关推荐

  1. java : enum、创建文件和文件夹、删除文件和文件夹、获得项目绝对路径、写入数据到excel中、java代码中两种路径符号写法、读取、写入text文件...

    java : enum http://www.cnblogs.com/hyl8218/p/5088287.html 创建文件和文件夹.删除文件和文件夹 http://www.cnblogs.com/m ...

  2. java 自定义函数的调用_Java/Android中的函数调用回调函数自定义回调函数

    在做Android自定义控件时遇到要自定义回调函数的问题,想想自己还暂时没有那么精深的技术,赶紧返过头回来再重新研究Java中回调函数的问题.然而不幸的是,网上太多杂乱的帖子和博客都是转来转去,而且都 ...

  3. Redis中两种持久化机制RDB和AOF

    redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失.幸好Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Ap ...

  4. Redis 中两种持久化机制详解

    Redis 持久化机制(快照.AOF) 快照 (Snapshot) 1. 客户端方式之 BGSAVE(多线程执行) 2. 客户端方式之 SAVE(单线程执行) 3. 服务器配置方式之 配置快照触发条件 ...

  5. 【c++】23.【函数指针】定义? 为什么不直接调用函数而要使用函数指针?

    1. 函数指针及其定义和用法,C语言函数指针详解 原文链接:http://c.biancheng.net/view/228.html 什么是函数指针 如果在程序中定义了一个函数,那么在编译时系统就会为 ...

  6. Nginx入门之两种handler函数的挂载方式

    请在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 接着上次的文章,今天研究<深入理解Nginx模块开发与架构解析>一书中给出的mytest的例子,发现和 /tengine.t ...

  7. 结构体中的函数指针(c语言里一种思想)

    阅读raft源码的时候看到结构体里面的void(*xx) 看不懂这个地方,看上去又像面向对象的类方法,但是这是c语言的结构体啊,了解了这是函数指针.小趴菜~ 一.函数指针 函数指针是指向函数的指针变量 ...

  8. python列表去重函数_对python中两种列表元素去重函数性能的比较方法

    测试函数: 第一种:list的set函数 第二种:{}.fromkeys().keys() 测试代码: #!/usr/bin/python #-*- coding:utf-8 -*- import t ...

  9. [转] 彻底了解指针数组,数组指针,以及函数指针,以及堆中的分配规则

    一 :关于指针和堆的内存分配 先来介绍一下指针: 指针一种类型,理论上来说它包含其他变量的地址,因此有的书上也叫它:地址变量.既然指针是一个类型,是类型就有大小,在达内的服务器上或者普通的PC机上,都 ...

最新文章

  1. MLIR中间表示和编译器框架
  2. Gridview][UpdateCommand的写法要点]
  3. psql where里有自定义函数慢_阿里P8架构师谈:MySQL慢查询优化、索引优化、以及表等优化总结...
  4. geoserver的api接口_geoserver REST使用
  5. python3 scarpy
  6. 《JavaScript高级程序设计》笔记总结
  7. GTK之任意拖动窗口中的按钮
  8. Redis 学习---(5)Redis 命令
  9. 零起点英语_【零起点英语】第100讲:The Cost of War 战争的代价
  10. 百度语义预训练ERNIE实现物流信息抽取任务
  11. linux下升级glibc-2.14问题
  12. 如何应对容器和云原生时代的安全挑战?
  13. 【机器学习系列】变分推断第二讲:基于Mean Field的变分推断解法
  14. 解决方案:Android开发基于rtmp视频直播
  15. vue在开发环境怎么兼容ie_vue 配置兼容ie浏览器
  16. 目标检测论文解读复现之十四:一种基于残差网络优化的航拍小目标检测算法
  17. 第一章 教育基础(06 小学课程)
  18. 2.15 随机存取存储器与只读存储器
  19. STM32——USART串口
  20. Linux上启动mysql不成功

热门文章

  1. 界面怎么使用pip_从零开始学Python——10Pyinstaller的使用
  2. linux查看msf安装目录,linux系统安装msf的过程详解
  3. php 前端控制器,前端控制器模式
  4. opencv函数copyto的用法
  5. CV算法复现(分类算法6/6):MobileNet(2017年V1,2018年V2,2019年V3,谷歌)
  6. 编译OpenCV 2+ with CUDA 9+
  7. Intro to Parallel Programming CUDA-第二单元
  8. 在Ubuntu 16.04.1 LTS上测试Linux AIO功能实录
  9. Ubuntu 14.04 64bit上安装LNMP环境
  10. 记一次shell脚本推后台stopped的问题