1、函数类型语法基础
函数三要素: 名称、参数、返回值 C语言中的函数有自己特定的类型 |
C语言中通过typedef为函数类型重命名 typedef type name(parameter list) typedef int f(int, int); typedef void p(int); |
函数指针 |
函数指针用于指向一个函数 函数名是函数体的入口地址 1)可通过函数类型定义函数指针: FuncType* pointer; 2)也可以直接定义:type (*pointer)(parameter list); pointer为函数指针变量名 type为指向函数的返回值类型 parameter list为指向函数的参数类型列表 |
函数指针语法梳理 //函数类型 //函数指针类型 //函数指针变量 数组指针语法梳理 //数组类型语法 //数组指针类型 //数组指针变量 |
typedef int(FUNC)(int); int test(int i) { return i * i; } void f() { printf("Call f()...\n"); } int main() { FUNC* pt = test;
void(*pf)() = &f;
pf(); (*pf)();
printf("Function pointer call: %d\n", pt(3)); } |
2、函数指针做函数参数
回忆指针做函数参数 一级指针做函数参数、二级。。。。、三级 |
当函数指针 做为函数的参数,传递给一个被调用函数, 被调用函数就可以通过这个指针调用外部的函数,这就形成了回调 |
3、练习 int add(int a, int b); int libfun(int (*pDis)(int a, int b)); int main(void) { int (*pfun)(int a, int b); pfun = add; libfun(pfun); } int add(int a, int b) { return a + b; } int libfun(int (*pDis)(int a, int b)) { int a, b; a = 1; b = 2; add(1,3) //直接调用add函数 printf("%d", pDis(a, b)); //通过函数指针做函数参数,间接调用add函数 //思考 这样写 pDis(a, b)有什么好处? } //剖析思路 //1函数的调用 和 函数的实现 有效的分离 //2 C++的多态,可扩展 现在这几个函数是在同一个文件当中 假如 int libfun(int (*pDis)(int a, int b)) 是一个库中的函数,就只有使用回调了,通过函数指针参数将外部函数地址传入 来实现调用 函数 add 的代码作了修改,也不必改动库的代码,就可以正常实现调用 便于程序的维护和升级 |
回调函数思想:
结论:回调函数的本质:提前做了一个协议的约定(把函数的参数、函数返回值提前约定)
请思考:C编译器通过那个具体的语法,实现解耦合的?
C++编译器通过多态的机制(提前布局vptr指针和虚函数表,找虚函数入口地址来实现)
3、函数指针正向调用
被调用函数和主调函数在同一文件中(用来教学,没有任何意义) |
2、函数指针做函数参数 被调用函数和主调函数不在同一个文件中、模块中。 难点:理解被调用函数是什么机制被调用起来的。框架 框架提前设置了被调用函数的入口(框架提供了第三方模块入口地址的集成功能) 框架具备调用第三方模块入口函数 |
typedef int (*EncDataFunc)(unsigned char *inData,int inDataLen,unsigned char *outData,int *outDataLen,void *Ref, int RefLen); int MyEncDataFunc(unsigned char *inData,int inDataLen,unsigned char *outData,int *outDataLen,void *Ref, int RefLen) { int rv = 0; char *p = "222222222222"; strcpy(outData, p); *outDataLen = strlen(p); return rv; } int Send_Data(EncDataFunc encDataFunc, unsigned char *inData, int inDataLen, unsigned char *outData, int *outDatalen) { int rv = 0; if (encDataFunc != NULL) { rv = encDataFunc(inData, inDataLen, outData, outDatalen, NULL, 0); if (rv != 0) { printf("func encDataFunc() err.\n"); return rv; } } return rv; } int main() { int rv = 0; EncDataFunc encDataFunc = NULL; encDataFunc = MyEncDataFunc; // 第一个调用 { unsigned char inData[2048]; int inDataLen; unsigned char outData[2048]; int outDatalen; strcpy(inData, "1111"); inDataLen = strlen(inData); rv = encDataFunc(inData,inDataLen, outData, &outDatalen, NULL, 0); if (rv != 0) { printf("edf err .....\n"); } else { printf("edf ok \n"); printf("%s \n", outData); } } { unsigned char inData[2048]; int inDataLen; unsigned char outData[2048]; int outDatalen; strcpy(inData, "3333"); inDataLen = strlen(inData); rv = Send_Data(MyEncDataFunc, inData, inDataLen, outData, &outDatalen); if (rv != 0) { printf("func Send_Data err:%d", rv); return rv; } printf("%s \n", outData); } getchar(); } |
4、函数指针反向调用
回调函数效果展示。
5、C动态库升级成框架案例
C语言版本Socket动态库升级成框架集成第三方产品
简称:C动态库升级成框架案例
名字解释
动态库:抽象类一个套接口,单独封装成模块,供别人调用;无法扩展。
框架:能自由的扩展
案例背景:一般的企业信息系统都有成熟的框架,可以有C语言写,也可以由C++语言。软件框架一般不发生变化,能自由的集成第三方厂商的产品。
案例需求:在socket通信库中,完成数据加密功能,有n个厂商的加密产品供你选择,如何实现动态库和第三个厂商产品的解耦合。
提醒:C++通过抽象类,也就是面向抽象类编程实现的(相当于C++编译器通过多态机制,已经很好用了。提前布局vptr指针、虚函数表;调用是迟绑定完成。),
C语言中如何实现哪?
案例要求: 1)能支持多个第三方厂商加密产品的入围
2)企业信息系统框架不轻易发生框架
需求实现思路分析
思考1:企业信息系统框架、第三方产品如何分层
思考2:企业信息系统框架,如何自由集成第三方产品
(软件设计:模块要求松、接口要求紧)
思考3:软件分层确定后,动态库应该做什么?产品入围厂商应该做什么?
以后,开发企业信息系统框架的程序员,应该做什么?
第三方产品入围应该做什么?
编码实现
- 动态库中定义协议,并完成任务的调用
typedef int (*EncData)(unsigned char *inData,int inDataLen,unsigned char *outData,int *outDataLen,void *Ref, int RefLen);
typedef int (*DecData)(unsigned char *inData,int inDataLen,unsigned char *outData,int *outDataLen,void *Ref, int RefLen);
- 加密厂商完成协议函数的编写
- 对接调试。
- 动态库中可以缓存第三方函数的入口地址,也可以不缓存,两种实现方式。
案例总结
回调函数:利用函数指针做函数参数,实现的一种调用机制,具体任务的实现者,可以不知道什么时候被调用。
回调机制原理:
当具体事件发生时,调用者通过函数指针调用具体函数
回调机制的将调用者和被调函数分开,两者互不依赖
任务的实现 和 任务的调用 可以耦合 (提前进行接口的封装和设计)