函数指针--Nginx和Redis中两种回调函数写法
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)所以为了区别各个信号所以只能将这些东西定义为这么复杂的声明了。
相关文章:
人生苦短,不光要用Python,还要在VSCode里用
作者 | imbennyguo出品 | CSDN博客在程序员圈子里,Visual Studio Code(以下简称VSCode)可以说是目前最火的代码编辑器之一了。它是微软出品的一款可扩展的轻量级开源编辑器,并且支持全平台系统。这些特性使得VSCode颇受欢迎&#…

从XXX 产品的失败谈起
从XXX 产品的失败谈起 从去年年中公司开始秘密研究一项高级产品,以替换陈旧体系下的老款产品,为此,创建了一个产品开发部 门,由七八个富有本行业开发经验的全职成员构成,经过一年多的努力,即将投入市场了&…

C++ 经常使用类 string类
6.3.2使用string对象 string word"I love China" *链接字符串* string descriptionadjective " " word; _Note_: 不能连接两个字符串字面量,以下的语句是错误的 string test "I have" "a dream"; 6.3.3訪问字符串中的字…

Nginx源码分析--字符串处理
ngx_string.cvoid ngx_strlow(u_char *dst, u_char *src, size_t n); 将src的前n个字符转换成小写存放在dst字符串当中,调用者需要保证dst指向的空间大于等于n。操作不会对原字符串产生变动。如要更改原字符串,可以:ngx_str_t str ngx_strin…
信息保留的二值神经网络IR-Net,落地性能和实用性俱佳 | CVPR 2020
出品 | AI科技大本营(ID:rgznai100)导语:在CVPR 2020上,商汤研究院链接与编译组和北京航空航天大学刘祥龙老师团队提出了一种旨在优化前后向传播中信息流的实用、高效的网络二值化新算法IR-Net。不同于以往二值神经网络大多关注量…

Jq-table最后一行添加样式
豪情姓名QQE-mail豪情249056406jikeytanggmail.comtony1234567puaggmail.comtony1234567puaggmail.comtony1234567puaggmail.comtony1234567puaggmail.com豪情啊不得了真是的运行代码

Same binary weight (位运算)
题目描述 The binary weight of a positive integer is the number of 1s in its binary representation.for example,the decmial number 1 has a binary weight of 1,and the decimal number 1717 (which is 11010110101 in binary) has a binary weight of 7.Give a positi…

Nginx源码分析--基本数据类型的别名
typedef intptr_t ngx_int_t; typedef uintptr_t ngx_uint_t; typedef intptr_t ngx_flag_t; intptr_t在 # /usr/include/stdint.h 中,下面是stdint.h部分代码 #ifndef _STDINT_H #define _STDINT_H 1#include <features.h> #include…

EnterpriseDB Migration 迁移工具使用测试(2)
下面我们来测试EnterpriseDB Migration 工具对于Oracle 大对象(LOB)的迁移情况; 首先在在Oracle实例Scott模式下创建具有LOB对象的表,如: SQL> create table tlob (t1 int primary key,t2 clob,t3 blob); Table created. -- 并填充数据 SQL> begin…
生物学的机器学习:使用K-Means和PCA进行基因组序列分析 COVID-19接下来如何突变?...
作者 | Andre Ye译者 | 孟翔杰来源 | DeepHub IMBA许多人没有想到,病毒就像地球上为生存而挣扎的其他生物一样,它们会进化或变异。只要看一看人类病毒来源的蝙蝠携带的病毒RNA序列片段即可。AAAATCAAAGCTTGTGTTGAAGAAGTTACAACAACTCTGGAAGAAACTAAGTT…以及…

sql server时间转换
--getdate 获取当前时间select getdate()--dateadd 原有时间加: 2013-02-17 13:20:16 此时间加12个月 select dateadd(MONTH,12,2013-02-17 13:20:16) --返回:2014-02-17 13:20:16.000 (参数month可以改为 day,year等日期加相应…
cgdb安装与使用
官网:http://cgdb.github.io/ 安装 # wget http://cgdb.me/files/cgdb-0.6.7.tar.gz # tar -zvxf cgdb-0.6.7.tar.gz # cd cgdb-0.6.7 # ./configure --prefix/usr/local # make && make install 使用 1:cgdb分为上下两栏,…

[MFC]多线程传参问题 [AfxBeginThread]
前言MFC中使用多线程比较简单,大家都推荐用AfxBeginThread来实现,但使用过程中确遇到传入this之后不能得到对象内部字段数据的问题。正文一、问题代码1.1 Test.h#pragmaonceclassCTest{public: CTest(void); ~CTest(void); voidThreadMethod…
学习Python,这22个包怎能不掌握?
作者 | Erik-Jan van Baaren译者 | 弯月,责编 | 屠敏头图 | CSDN 下载自东方 IC出品 | CSDN(ID:CSDNnews)以下为译文:如今全球各个行业内 Python 的使用状况怎么样呢?这个问题就是我写这篇文章的初衷。我找…

JSTL标签库的一些基础实例
如题所示,只是一些简单的入门实例,代码如下:<% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%taglib prefix"c" uri"http://java.sun.com/jsp/jstl/core"%…
旷视提Circle Loss,统一优化视角,革新深度特征学习范式 | CVPR 2020
作者 | 旷视研究院本文是旷视 CVPR 2020的被收录论文解读第。它提出用于深度特征学习的Circle Loss,从相似性对优化角度正式统一了两种基本学习范式(分类学习和样本对学习)下的损失函数。通过进一步泛化,Circle Loss 获得了更灵活…

收藏好玩的网站
1.输名字出照片 http://turnyournameintoaface.com/?name2.看google页面可以做成这样http://mrdoob.com/projects/chromeexperiments/google_gravity/3. 有趣的在线人脸变动物(川剧中有变脸,其实你也可以滴!)http://youwild.org/…
GDB 格式化结构体输出
set print address set print address on 打开地址输出,当程序显示函数信息时,GDB会显出函数的参数地址。系统默认为打开的, show print address 查看当前地址显示选项是否打开。 set print array set print array on 打开数组显示ÿ…

Kinect For Windows V2开发日志九:侦测并绘制人体骨架
简介 在上一篇《侦测、追踪人体骨架》里,介绍了关节点的使用办法,这一篇记录将关节点与OpenCV结合的绘图方法。 代码 #include <iostream> #include <opencv2\imgproc.hpp> #include <opencv2\calib3d.hpp> #include <opencv2\high…

拨号连接或 ××× 连接的错误代码列表
本文列出了在使用Windows 2000、Windows XP 或 Windows Server 2003 作为客户机,建立拨号连接或 连接时可能收到的错误代码。注意:只有在尝试连接到正运行Windows 2000 或更高版本的路由和远程访问服务器时,才会看到编号高于 900 的错误代码…
“程序员数学不行,干啥都不行!”高级开发:90%都是瞎努力!
之前有很多读者留言向我们反馈:数学不好做算法优化,每次遇到不一样的问题就不会了,要是赶上面试基本就凉凉了。平时很难搞懂像数据结构、算法、复杂的语句等等,因为这些核心原理都是数学。而且光会基础数学远远不够,还…
Redis源码分析--lookupKey函数查看value值
lookupKey函数查看value值 robj *lookupKey(redisDb *db, robj *key) {dictEntry *de dictFind(db->dict,key->ptr);if (de) {robj *val dictGetVal(de);/* Update the access time for the ageing algorithm.* Dont do it if we have a saving child, as this will t…

PHP获取时间排除周六、周日的两个方法
//方法一: <?php $now time(); //指定日期用法 $now strtotime(2014-01-08) ; $day 3600*24; $total 12;$days array() ;for ($i2;$i<$total;$i) {$timer $now$day*$i;$num date("N",$timer)-2; //周一开始if($num>-1 and $num<3){if(c…

SMS2003 SP3+SQL Server2000 SP4部署(下)
<?xml:namespace prefix o />SMS2003 SP3SQL Server2000 SP4部署(下)<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />E.SMS2003安装在berlin上放入SMS2003光盘镜像,选择SMS2003出现SMS2003安装向导ÿ…

redis源码分析--zslRandomLevel位运算解析
与&运算 0376的二进制就是1111 1110,二进制的与运算规则是,只有两个数同时为1时,则结果才为1,只要有一个数为0,则结果就为0。比如1&11; 1&00; 0&10; 0&00;因此把一个字符与二进制1111 1110进行与…
从零开始构建:使用CNN和TensorFlow进行人脸特征检测
作者 | Shubham Panchal译者 | 孟翔杰来源 | DeepHub IMBA出品 | AI科技大本营(rgznai100)人脸检测系统在当今世界中具有巨大的用途,这个系统要求安全性,可访问性和趣味性!今天,我们将建立一个可以在脸上绘…

scope重定义
.directive(myAttr, function() {return {restrict: E,scope: {customerInfo: info},template: Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br> Name: {{vojta.name}} Address: {{vojta.address}}}; }); directive中的几个属性: restric…
基于轮廓调整的SOTA实例分割方法,速度达32.3fps | CVPR 2020
作者 | VincentLee来源 | 晓飞的算法工程笔记介绍实例分割是许多计算机视觉任务中的重要手段,目前大多数的算法都采用在给定的bbox中进行pixel-wise分割的方法。受snake算法和Curve-GCN的启发,论文采用基于轮廓的逐步调整策略,提出了Deep sna…

Redis运行流程源码解析
原文作者:凡趣科技 pesiwang 原文地址:http://blog.nosqlfan.com/html/4007.html 本文分析源码基于 Redis 2.4.7 stable 版本。 概述 Redis通过定义一个 struct redisServer 类型的全局变量server 来保存服务器的相关信息(比如:…

2010年5月blog汇总:OpenExpressApp、其他
OpenExpressApp 信息系统开发平台OpenExpressApp - 框架待完善工作事项信息系统开发平台OpenExpressApp - 报表模块支持ReportObjectView信息系统开发平台OpenExpressApp - 从compositewpf到MEF信息系统开发平台OpenExpressApp - …