当前位置: 首页 > 文档资料 > C 语言程序设计 >

函数指针

优质
小牛编辑
125浏览
2023-12-01

函数指针基础语法

函数指针用于指向一个函数,函数名是函数体的入口地址。函数指针可以实现面向对象编程,可以实现软件分层设计(回调函数)。

#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

//数组指针 语法 梳理
//定义一个数组类型   
//int a[10];

//定义一个指针数组类型 
//定义一个指向数组类型的指针,数组类的指针

void main01()
{
    int a[10]; //a代表的是数组首元素的地址  &a代表整个数组的地址  a+1 4  &a+1步长 40
    {
        //定义一个数组类型
        typedef int (myTypeArray)[10];
        myTypeArray myArray;
        myArray[0] = 10;
        printf("%d \n", myArray[0]);
    }

    {
        //定义一个数组指针类型 
        typedef int (*PTypeArray)[10];  //int *p 

        PTypeArray myPArray; //sizeof(int) *10
        myPArray = &a;
        //int b = 10;
        //int *p = NULL;
        //p = &b;
        (*myPArray)[0] = 20;

        printf("a[0]: %d \n", a[0]);
    }

    {
        //定义一个指向数组类型的指针,数组类的指针
        int (*MyPointer)[10]; //变量 告诉C编译器 给我分配内存
        MyPointer = &a;
        (*MyPointer)[0] = 40;
        printf("a[0]: %d \n", a[0]);
    }

    system("pause");
    return ;
}

//函数指针语法梳理
//1 如何定义一个函数类型
//2 如何定义一个函数指针类型
//3 如何定义一个 函数指针  (指向一个函数的入口地址)

int add(int a, int b)
{
    printf("func add ....\n");
    return a +b;
}

void main02()
{
    add(1, 2); //直接调用调用,函数名就是函数的入口地址 

    //定义一个函数类型
    {
        typedef int (MyFuncType)(int a, int b); //定义了一个类型
        MyFuncType *myPointerFunc = NULL; //定义了一个指针, 指向某一种类的函数

        myPointerFunc  = &add;  //细节
        myPointerFunc(3, 4); //间接调用

        myPointerFunc  = add;  //细节,C 过程 兼容历史版本的原因
        myPointerFunc(3, 4); //间接调用
    }

    //定义一个函数指针类型
    {
        typedef int (*MyPointerFuncType)(int a, int b); //int * a = NULL;
        MyPointerFuncType myPonterFunc; //定义一个指针
        myPonterFunc = add;
        myPonterFunc(5, 6);
    }

    //函数指针 
    {
        int (*MyPonterFunc)(int a, int b); //定义了一个变量
        MyPonterFunc = add;
        MyPonterFunc(7, 8);
    }

    system("pause");
}

函数指针做函数参数

当函数指针作为函数的参数,传递给一个被调用函数,被调用函数就可以通过这个指针调用外部的函数,这就形成了回调

#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int myadd(int a, int b)  //子任务的实现者
{
    printf("func add() do...\n");
    return a + b;
}
int myadd2(int a, int b)  //子任务的实现者
{
    printf("func add2() do...\n");
    return a + b;
}
int myadd3(int a, int b)  //子任务的实现者
{
    printf("func add3() do...\n");
    return a + b;
}
int myadd4(int a, int b)  //子任务的实现者
{
    printf("func add4() do...\n");
    return a + b;
}

//定义了一个类型 
typedef int (*MyTypeFuncAdd)(int a, int b);

//函数指针做函数参数
int MainOp(MyTypeFuncAdd myFuncAdd)
{
    int c = myFuncAdd(5, 6);
    return c;
}

// int (*MyPointerFuncAdd)(int a, int b)
int MainOp2(int (*MyPointerFuncAdd)(int a, int b) )
{
    int c = MyPointerFuncAdd(5, 6); //间接调用
    return c;
}

//间接调用 
//任务的调用和任务的编写可以分开
void main()
{
    /*
    MyTypeFuncAdd  myFuncAdd = NULL;
    myadd(1, 2); //直接调用

    myFuncAdd = myadd; 
    myFuncAdd(3, 4); //间接调用 

    MainOp2(myadd);
    MainOp(myadd);
    */

    //在mainop框架 没有发生任何变化的情况下
    MainOp(myadd2);
    MainOp(myadd3);
    MainOp(myadd4);

    system("pause");
    return ;
}

回调函数:函数的调用和函数的实现 有效的分离

回调函数的本质:提前做了一个协议的约定(把函数的参数、函数返回值提前约定)

请思考:C编译器通过那个具体的语法,实现解耦合的?

C++编译器通过多态的机制(提前布局vptr指针和虚函数表,找虚函数入口地址来实现)

回调函数

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

回调机制原理:

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

函数指针正向调用

函数指针做函数参数,被调用函数和主调函数不在同一个文件中、模块中。

//函数指针类型  
//客户端初始化 获取handle上下文
typedef int (*CltSocketInit)(void **handle /*out*/); 

//客户端发报文
typedef int (*CltSocketSend)(void *handle /*in*/, unsigned char *buf /*in*/,  int buflen /*in*/);

//客户端收报文
typedef int (*CltSocketRev)(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/);

//客户端释放资源
typedef int (*CltSocketDestory)(void *handle/*in*/);

void C函数指针正向调用Dlg::OnBnClickedButton1()
{
    // TODO: 在此添加控件通知处理程序代码
    AfxMessageBox("ddddd");


    HINSTANCE hInstance;
      //用函数指针类型定义函数指针变量 
    CltSocketInit        cltSocketInit;
    CltSocketSend        cltSocketSend;
    CltSocketRev        cltSocketRev;
    CltSocketDestory    cltSocketDestory;

    //找函数的入口地址  
    hInstance=::LoadLibrary("c:/socketclient.dll");
    cltSocketInit =(CltSocketInit)::GetProcAddress(hInstance, "cltSocketInit");
    cltSocketSend =(CltSocketSend)::GetProcAddress(hInstance, "cltSocketSend");
    cltSocketRev =(CltSocketRev)::GetProcAddress(hInstance, "cltSocketRev");
    cltSocketDestory =(CltSocketDestory)::GetProcAddress(hInstance, "cltSocketDestory");

    void            *handle = NULL;
    unsigned char     buf[2048];
    int                buflen = 9;
    unsigned char    out[2048];
    int                outlen;
    int                ret = 0;

    strcpy((char *)buf, "hello");

    ret = cltSocketInit(&handle);
    ret =  cltSocketSend(handle, buf, buflen);
    ret = cltSocketRev(handle, out, &outlen);
    ret = cltSocketDestory(handle);
}

函数指针反向调用

回调函数

socketclient.c

typedef struct _SCK_HANDLE {
    char        version[16];
    char        serverip[16];
    int        serverport;
    unsigned char *    buf ;
    int                buflen;
}SCK_HANDLE;

//客户端初始化
ITCAST_FUNC_EXPORT(int)
cltSocketInit(void **handle)
{
    SCK_HANDLE        *sh = NULL;
    int                rv = 0;

    if (handle == NULL)
    {
        rv = -1;
        return rv;
    }
    //分配内存并初始化
    sh = (SCK_HANDLE *)malloc(sizeof(SCK_HANDLE));
    if (sh == NULL)
    {
        rv = -2;
        return rv;
    }
    memset(sh, 0, sizeof(SCK_HANDLE));

    //域赋值
    strcpy(sh->version, "0.0.1");
    strcpy(sh->serverip, "192.168.0.211");
    sh->serverport = 8888;

    //传出
    *handle = sh;
    return rv;
}

//客户端发报文
ITCAST_FUNC_EXPORT(int)
cltSocketSend(void *handle, unsigned char *buf,  int buflen)
{
    int                rv = 0;
    SCK_HANDLE        *sh = NULL;

    if (handle == NULL)
    {
        rv = -4;
        return rv;
    }
    if (buf == NULL || buflen<=0)
    {
        rv = -5;
        return rv;
    }

    sh = (SCK_HANDLE *)handle;
    sh->buf = (unsigned char *)malloc(sizeof(char)*buflen);
    if (sh->buf == NULL)
    {
        rv = -6;
        return rv;
    }

    //把发送的报文数据,存储 handle 上下文之中
    memcpy(sh->buf, buf, buflen);
    sh->buflen = buflen;

    return rv;
}
//客户端收报文
ITCAST_FUNC_EXPORT(int)
cltSocketRev(void *handle, unsigned char *buf, int *buflen)
{
    int            rv = 0;
    SCK_HANDLE    *sh = NULL;

    if (handle == NULL)
    {
        rv = -4;
        return rv;
    }
    if (buflen == NULL)
    {
        rv = -5;
        return rv;
    }

    sh = (SCK_HANDLE *)handle;

    //赋值 把上下文中的数据,copy到buf空间中
    //支持二次调用,第一次调用求长度 第二次调用可以把数据copy buf中
    if (buf != NULL)
    {
        memcpy(buf, sh->buf, sh->buflen);
        //buf[ci->buflen] = '\0';
    }
    *buflen = sh->buflen;

    return rv;
}
//客户端释放资源
ITCAST_FUNC_EXPORT(int)
cltSocketDestory(void *handle)
{
    SCK_HANDLE *sh = NULL;
    sh = handle; 
    if (sh != NULL)
    {
        free(sh->buf);
        free(sh);
    }
    return 0;
}

//第二套api实现

//客户端初始化
ITCAST_FUNC_EXPORT(int)
cltSocketInit2(void **handle) //5day after
{
    SCK_HANDLE        *sh = NULL;
    int                rv = 0;

    if (handle == NULL)
    {
        rv = -1;
        return rv;
    }
    //分配内存并初始化
    sh = (SCK_HANDLE *)malloc(sizeof(SCK_HANDLE));
    if (sh == NULL)
    {
        rv = -2;
        return rv;
    }
    memset(sh, 0, sizeof(SCK_HANDLE));

    //域赋值
    strcpy(sh->version, "0.0.1");
    strcpy(sh->serverip, "192.168.0.211");
    sh->serverport = 8888;

    //传出
    *handle = sh;

    return rv;
}

//客户端发报文
ITCAST_FUNC_EXPORT(int)
cltSocketSend2(void *handle, unsigned char *buf,  int buflen)
{
    int                rv = 0;
    SCK_HANDLE        *sh = NULL;

    if (handle == NULL)
    {
        rv = -4;
        return rv;
    }
    if (buf == NULL || buflen<=0)
    {
        rv = -5;
        return rv;
    }

    sh = (SCK_HANDLE *)handle;
    sh->buf = (unsigned char *)malloc(sizeof(char)*buflen);
    if (sh->buf == NULL)
    {
        rv = -6;
        return rv;
    }

    //把发送的报文数据,存储 handle 上下文之中
    memcpy(sh->buf, buf, buflen);
    sh->buflen = buflen;

    return rv;
}
//客户端收报文
ITCAST_FUNC_EXPORT(int)
cltSocketRev2(void *handle, unsigned char **buf, int *buflen)
{
    int            rv = 0;
    SCK_HANDLE    *sh = NULL;

    if (handle == NULL)
    {
        rv = -4;
        return rv;
    }
    if (buflen == NULL)
    {
        rv = -5;
        return rv;
    }
    sh = (SCK_HANDLE *)handle;


    //分配内存数据传出
    *buf = (char *)malloc(sh->buflen);
    if (*buf == NULL)
    {
        rv = -6;
        return rv;
    }
    memcpy(*buf, sh->buf, sh->buflen);

    *buflen = sh->buflen;

    return rv;
}

ITCAST_FUNC_EXPORT(int)
cltSocketRev2_Free(unsigned char **buf)
{
    int rv = 0;
    unsigned char * tmp = *buf;
    if (buf == NULL)
    {
        rv = -7;
        return rv;
    }
    if (tmp != NULL)
    {
        free(tmp);
    }
    *buf = NULL;
}

//客户端释放资源
ITCAST_FUNC_EXPORT(int)
cltSocketDestory2(void **handle)
{
    SCK_HANDLE *sh = NULL;
    sh = *handle; 
    if (sh != NULL)
    {
        free(sh->buf);
        free(sh);
    }
    *handle = NULL;
    return 0;
}

test.c

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "socketclientdll.h"

//typedef int (*EncData)(unsigned char *inData,int inDataLen,unsigned char *outData,int *outDataLen,void *Ref, int RefLen);

int myEncData(unsigned char *inData,
              int inDataLen,
              unsigned char *outData,
              int *outDataLen,
              void *Ref, 
              int RefLen)
{
    memcpy(outData, "1111", 10);
    *outDataLen = 10;
    return 0;
}

int main()
{
    int                rv = 0;
    void            *handle = NULL;
    unsigned char    buf[2048];
    int                buflen = 0;

    unsigned char    buf2[2048] = {0};
    int                buflen2 = 0;

    strcpy(buf, "aaaa");

    buflen = 10;
    rv = cltSocketInit(&handle);
    if (rv != 0)
    {
        printf("func cltSocketInit():%d", rv);
        return rv;
    }
    /*
    // cvtres.exe
    rv = clitSocket_SetEncFunc(handle, myEncData, NULL, 0);
    if (rv != 0)
    {
        printf("func cltSocketInit():%d", rv);
        goto End;
    }
    */

    rv = cltSocketSend(handle, buf,  buflen);
    if (rv != 0)
    {
        printf("func cltSocketSend():%d", rv);
        goto End;
    }
    /*
    rv = cltSocketSend_enc(handle, buf,  buflen, myEncData, NULL, 0);
    if (rv != 0)
    {
        printf("func cltSocketSend_enc():%d", rv);
        goto End;
    }
    */

    rv = cltSocketRev(handle, buf2 , &buflen2);
    if (rv != 0)
    {
        printf("func cltSocketRev():%d", rv);
        goto End;
    }
    printf("\n%s", buf2);

End:

    rv = cltSocketDestory(handle);
    if (rv != 0)
    {
        printf("func cltSocketDestory():%d", rv);
        return rv;
    }

    //system("pause");
    return 0;
}