深入理解C语言-函数指针

函数指针在C++中有着重要的应用,函数的函数名其本质就是代表一个地址,这个地址叫做函数入口,得到这个地址就可以对这个函数进行各种操作。

函数类型基础

  • 函数三要素: 名称、参数、返回值
  • C语言中的函数有自己特定的类型
  • C语言中通过typedef为函数类型重命名,类似于定义数组
typedef type name[m];//定义数组类型
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));
}

函数指针做函数参数

当函数指针做为函数的参数,传递给一个被调用函数,
被调用函数就可以通过这个指针调用外部的函数,这就形成了回调
其本质就是把函数入口地址和函数参数传递给调用函数,也即对函数类型做了限定

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)
    printf("%d", pDis(a, b));
}

假如int libfun(int (*pDis)(int a, int b))是一个库中的函数,就只有使用回调了,通过函数指针参数将外部函数地址传入来实现调用
函数 add 的代码作了修改,也不必改动库的代码,就可以正常实现调用便于程序的维护和升级

函数指针正向调用

  1. 函数指针做函数参数,调用方式
    被调用函数和主调函数在同一文件中,这种方式在实际开发中并不会用到
  2. 函数指针做函数参数
    被调用函数和主调函数不在同一个文件中、模块中。
    难点:理解被调用函数是什么机制被调用起来的。框架
    框架提前设置了被调用函数的入口(框架提供了第三方模块入口地址的集成功能)
    框架具备调用第三方模块入口函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

函数指针反向调用

利用函数指针实现的一种调用机制
具体任务的实现者,可以不知道什么时候被调用
回调函数是利用函数指针实现的一种调用机制

回调机制原理

  • 当具体事件发生时,调用者通过函数指针调用具体函数
  • 回调机制的将调用者和被调函数分开,两者互不依赖
  • 任务的实现和任务的调用可以耦合(提前进行接口的封装和设计)

需要重新审视 dll和其他第三方厂商 接口之间的关系

  • 动态库可以集成第三方厂商的业务功能
  • 动态库(框架)不经常改动,能兼容后来的业务模型
  • 动态库和第三方业务模型 模块之间 松耦合

动态库变成框架,需要做的工作

  1. 第三方业务入口传进来(回调函数的入口地址传进来)
  2. 加密 解密 业务模型抽象
    实现动态库加密解密业务模型抽象
    通过定义函数指针类型;函数指针做函数参数实现
posted @ 2019-04-02 23:36  cj5785  阅读(243)  评论(0编辑  收藏  举报