一、隐式
在讲反弹表达式以后,他们须要介绍隐式。
他们都晓得,C词汇的肉体是操作符,他们时常采用auth操作符,字符串操作符,内部结构体操作符等
int *p1; char *p2; STRUCT *p3; //STRUCT为他们表述的内部结构体但好似他们一般来说极少采用隐式,他们一般来说采用表达式都是间接采用表达式调用。
上面他们来介绍呵呵隐式的基本概念和采用形式。
1.基本概念
隐式是对准表达式的操作符表达式。
一般来说他们说的操作符表达式是对准两个auth、字符串型或字符串等表达式,而隐式是对准表达式。
隐式能像一般来说表达式那样,用作调用表达式、传达模块。
隐式的表述形式为:
表达式codice类别 (* 操作符表达式名) (表达式模块条目);
“表达式codice类型”则表示该操作符表达式能对准具备甚么codice类别的表达式;“表达式模块条目”则表示该操作符表达式能对准具备甚么模块条目的表达式。那个模块条目中只须要写表达式的模块类别方可。
他们看见,隐式的表述是将“表达式新闻稿”中的“表达式名”换成“(操作符表达式名)”。但这里须要注意的是:“(操作符表达式名)”两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是表述隐式而是两个表达式新闻稿了,即新闻稿了两个codice类别为操作符型的表达式。
那么怎么判断两个操作符表达式是对准表达式的操作符表达式还是对准表达式的操作符表达式呢?首先看表达式名前面有没有“”,如果有“”说明是操作符表达式;其次看表达式名的后面有没有带有形参类别的圆括号,如果有是对准函数的操作符表达式,即隐式,如果没有是对准表达式的操作符表达式。
最后须要注意的是,对准表达式的操作符表达式没有 ++ 和 – 运算。
一般来说为了方便采用,他们会选择
typedef 表达式codice类别 (* 操作符表达式名) (表达式模块条目);
比如
typedef int (*Fun1)(int);//新闻稿也可写成int (*Fun1)(int x),但习惯上一般来说不这样。 typedef int (*Fun2)(int, int);//模块为两个auth,codice为auth typedef void (*Fun3)(void);//无模块和codice typedef void* (*Fun4)(void*);//模块和codice都为void*操作符2,如何用表达式操作符调用表达式
给大家举两个例子:
int Func(int x); /*新闻稿两个表达式*/ int (*p) (int x); /*表述两个隐式*/ p = Func; /*将Func表达式的首地址赋给操作符表达式p*/p = &Func;/*将Func表达式的首地址赋给操作符表达式p*/赋值时表达式 Func 不带括号,也不带模块。由于表达式名 Func 代表表达式的首地址,因此经过赋值以后,操作符表达式 p 就对准表达式 Func() 代码的首地址了。
上面来写两个程序,看了那个程序你们就明白隐式怎么采用了:
#include <stdio.h> int Max(int, int); //表达式新闻稿 int main(void) { int(*p)(int, int); //表述两个隐式 int a, b, c; p = Max; //把表达式Max赋给操作符表达式p, 使p对准Max表达式 printf(“please enter a and b:”);scanf(“%d%d”, &a, &b); c = (*p)(a, b); //通过隐式调用Max表达式 printf(“a = %d\nb = %d\nmax = %d\n”, a, b, c);return 0; } int Max(int x, int y) //表述Max表达式 { int z; if (x > y) { z = x; } else{ z = y; }return z; } p = Max能换成 p = &Max c= (*p)(a, b) 能换成 c = p(a, b)嵌入式物联网须要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!
无偿分享大家两个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。
点击这里找小助理0元领取:加微信领取资料
3.隐式作为某个表达式的模块
既然隐式表达式是两个表达式,当然也能作为某个表达式的模块来采用的。
示例:
#include <stdio.h> #include <stdlib.h> typedef void(*FunType)(int); //前加两个typedef关键字,这样就表述两个名为FunType隐式类别,而不是两个FunType表达式。 //形式同 typedef int* PINT; void myFun(int x); void hisFun(int x); void herFun(int x); void callFun(FunType fp,int x); int main() { callFun(myFun,100);//传入隐式常量,作为反弹表达式 callFun(hisFun,200); callFun(herFun,300); return 0; } void callFun(FunType fp,int x) { fp(x);//通过fp的操作符执行传达进来的表达式,注意fp所指的表达式有两个模块 } void myFun(int x) { printf(“myFun: %d\n”,x); } void hisFun(int x) { printf(“hisFun: %d\n”,x); } void herFun(int x) { printf(“herFun: %d\n”,x); }输出:
4.隐式作为表达式返回类别
有了上面的基础,要写出返回类别为隐式的表达式应该不难了,上面那个例子是返回类别为隐式的表达式:
void (* func5(int, int, float ))(int, int) { … }在这里, func5 以 (int, int, float) 为模块,其返回类别为 void (\*)(int, int)。在C词汇中,表达式或者表达式的新闻稿也是两个大学问,想要介绍更多关于新闻稿的话题,能参考我以后的文章 – C专家编程》读书笔记(1-3章)。这本书的第三章花了整整一章的内容来讲解如何读懂C词汇的新闻稿。
5.隐式字符串
在开始讲解反弹表达式前,最后介绍呵呵隐式字符串。既然隐式也是操作符,那他们就能用字符串来存放隐式。上面他们看两个隐式字符串的例子:
/* 形式1 */ void (*func_array_1[5])(int, int, float); /* 形式2 */ typedef void (*p_func_array)(int, int, float); p_func_array func_array_2[5];上面两种形式都能用来表述隐式字符串,它们表述了两个元素个数为5,类别是 *void (\*)(int, int, float)*的隐式字符串。
6.隐式总结
隐式常量 :Max;隐式表达式:p;数名调用如果都得如(*myFun)(10)这样,那书写与读起来都是不方便和不习惯的。所以C词汇的设计者们才会设计成又可允许myFun(10)这种形式地调用(这样方便多了,并与数学中的表达式形式那样)。隐式表达式也能存入两个字符串内。字符串的新闻稿形式:int (*fArray[10]) ( int );二、反弹表达式
1.甚么是反弹表达式
他们先来看看百度百科是如何表述反弹表达式的:
反弹表达式是两个通过隐式调用的表达式。如果你把表达式的操作符(地址)作为模块传达给另两个表达式,当那个操作符被用来调用其所对准的表达式时,他们就说这是反弹表达式。反弹表达式不是由该表达式的实现方间接调用,而是在特定的事件或条件发生时由另外的一方调用的,用作对该事件或条件进行响应。
这段话比较长,也比较绕口。上面我通过一幅图来说明甚么是反弹:
假设他们要采用两个排序表达式来对字符串进行排序,那么在主程序(Main program)中,他们先通过库,选择两个库排序表达式(Library function)。但排序算法有很多,有冒泡排序,选择排序,快速排序,归并排序。同时,他们也可能须要对特殊的对象进行排序,比如特定的内部结构体等。库表达式会根据他们的须要选择一种排序算法,然后调用实现该算法的表达式来完成排序工作。那个被调用的排序表达式是反弹表达式(Callback function)。
结合这幅图和上面对反弹表达式的解释,他们能发现,要实现反弹表达式,最关键的一点是要将表达式的操作符传达给两个表达式(上图中是库表达式),然后那个表达式就能通过那个操作符来调用反弹表达式了。注意,反弹表达式并不是C词汇特有的,几乎任何词汇都有反弹表达式。在C词汇中,他们通过采用隐式来实现反弹表达式。
我的理解是:把一段可执行的代码像模块传达那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做反弹。
如果代码立即被执行就称为同步反弹,如果过后再执行,则称之为异步反弹。
反弹表达式是两个通过隐式调用的表达式。如果你把表达式的操作符(地址)作为模块传达给另两个表达式,当那个操作符被用来调用其所对准的表达式时,他们就说这是反弹表达式。
反弹函数不是由该表达式的实现方间接调用,而是在特定的事件或条件发生时由另外的一方调用的,用作对该事件或条件进行响应。
2 为甚么要用反弹表达式?
因为能把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需晓得存在两个具备特定原型和限制条件的被调用表达式。
简而言之,反弹表达式是允许用户把须要调用的形式的操作符作为模块传达给两个表达式,以便该表达式在处理相似事件的时候能灵活的采用不同的形式。
反弹似乎只是表达式间的调用,和普通表达式调用没啥区别。
但仔细看,能发现两者之间的两个关键的不同:在反弹中,主程序把反弹表达式像模块那样传入库表达式。
这样一来,只要他们改变传进库表达式的模块,就能实现不同的功能,这样有没有觉得很灵活?并且当库表达式很复杂或者不可见的时候利用反弹表达式就显得十分优秀。
3 怎么采用反弹表达式?
int Callback_1(int a) ///< 反弹表达式1 { printf(“Hello, this is Callback_1: a = %d “, a); return 0; } int Callback_2(int b) ///< 反弹表达式2 { printf(“Hello, this is Callback_2: b = %d “, b); return 0; } int Callback_3(int c) ///< 反弹表达式3 { printf(“Hello, this is Callback_3: c = %d “, c); return 0; } int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的隐式表述 { Callback(x); } int main() { Handle(4, Callback_1); Handle(5, Callback_2); Handle(6, Callback_3); return 0; }如上述代码:能看见,Handle()表达式里面的模块是两个操作符,在main()表达式里调用Handle()表达式的时候,给它传入了表达式Callback_1()/Callback_2()/Callback_3()的表达式名,这时候的表达式名是对应表达式的操作符,也是说,反弹表达式其实是隐式的一种用法。
4.上面是两个四则运算的简单反弹表达式例子:
#include <stdio.h> #include <stdlib.h> /**************************************** * 隐式内部结构体 ***************************************/ typedef struct _OP { float (*p_add)(float, float); float (*p_sub)(float, float); float (*p_mul)(float, float); float (*p_div)(float, float); } OP; /**************************************** * 加减乘除表达式 ***************************************/ float ADD(float a, float b) { return a + b; } float SUB(float a, float b) { return a – b;} float MUL(float a, float b) { return a * b; } float DIV(float a, float b) { return a / b; } /**************************************** * 初始化隐式 ***************************************/ void init_op(OP *op) { op->p_add = ADD; op->p_sub = SUB; op->p_mul = &MUL; op->p_div = &DIV; } /**************************************** * 库表达式 ***************************************/ float add_sub_mul_div(float a, float b, float (*op_func)(float, float)) { return (*op_func)(a, b);} int main(int argc, char *argv[]) { OP *op = (OP *)malloc(sizeof(OP)); init_op(op); /* 间接采用隐式调用表达式 */ printf(“ADD = %f, SUB = %f, MUL = %f, DIV = %f\n”, (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2), (op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2)); /* 调用反弹表达式 */ printf(“ADD = %f, SUB = %f, MUL = %f, DIV = %f\n”, add_sub_mul_div(1.3, 2.2, ADD), add_sub_mul_div(1.3, 2.2, SUB), add_sub_mul_div(1.3, 2.2, MUL), add_sub_mul_div(1.3, 2.2, DIV)); return 0; }5. 反弹表达式实例(很有用)
两个GPRS模块联网的小项目,采用过的同学大概晓得2G、4G、NB等模块要想实现无线联网功能都须要经历模块上电初始化、注册网络、查询网络信息质量、连接服务器等步骤,这里的的例子是,利用两个状态机表达式(根据不同状态依次调用不同实现形式的表达式),通过反弹表达式的形式依次调用不同的表达式,实现模块联网功能,如下:
/********* 工作状态处理 *********/ typedef struct { uint8_t mStatus; uint8_t (* Funtion)(void); //隐式的形式 } M26_WorkStatus_TypeDef; //M26的工作状态集合调用表达式 /********************************************** ** >M26工作状态集合表达式***********************************************/ M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] = { {GPRS_NETWORK_CLOSE, M26_PWRKEY_Off }, //模块关机 {GPRS_NETWORK_OPEN, M26_PWRKEY_On }, //模块开机 {GPRS_NETWORK_Start, M26_Work_Init }, //管脚初始化 {GPRS_NETWORK_CONF, M26_NET_Config }, /AT指令配置 {GPRS_NETWORK_LINK_CTC, M26_LINK_CTC }, //连接调度中心 {GPRS_NETWORK_LINK_FEM, M26_LINK_FEM }, //连接前置机 {GPRS_NETWORK_COMM, M26_COMM }, //正常工作 {GPRS_NETWORK_GetSig {GPRS_NETWORK_RESTART, M26_RESET }, //模块重启 } /********************************************** ** >M26模块工作状态机,依次调用里面的12个表达式 ***********************************************/ uint8_t M26_WorkStatus_Call(uint8_t Start) { uint8_t i = 0; for(i = 0; i < 12; i++) { if(Start == M26_WorkStatus_Tab[i].mStatus) { return M26_WorkStatus_Tab[i].Funtion(); } } return 0; }所以,如果有人想做个NB模块联网项目,能copy上面的框架,只须要修改反弹表达式内部的具体实现,或者增加、减少反弹表达式,就能很简洁快速的实现模块联网。
原文链接:
https://mp.weixin.qq.com/s/yHYsHkn1DVdXIun2l27NYg转载自:嵌入式与Linux那些事
原文链接:C词汇反弹表达式,提高C基本功必不可少