c语言函数指针与回调函数

目录

一、指针

1、指针

2、野指针与空指针

3、指针的算术运算

4、指针数组:(多个地址)由指针变量组成的数组。

5、多级指针:C 允许指向指针的指针。

6、传递指针给函数:通过引用或地址传递参数,使传递的参数在调用函数中被改变。

7、从函数返回指针(地址)

二、函数指针

1、声明

2、定义

3、调用:可直接用指针调用。

三、回调函数


一、指针

1、指针

指针:指向(/存储)其他变量的地址,该变量的数据类型需与指针的数据类型保持一致。

image-20240312105002379

等价于:

int *p = &var_runoob;

2、野指针与空指针

野指针:没有为指针附地址,此时指针任意指向一个地址,不利于程序安全。

空指针:指针附NULL,地址为0x0。可用于if判断。

注:free()内存后指针赋 NULL。

3、指针的算术运算

对指针进行四种算术运算:++、--、+、- ;指向下一个元素的存储单元,跳跃的字节数取决于所指向变量数据类型长度。

例:

int a[5];

int *p;

p = a;

则*p = a[0], *(p+1) = a[1]

4、指针数组:(多个地址)由指针变量组成的数组。

声明:[]的优先级高于*,因此先是数组,后为指针数组。

const char *names[] = {
                   "Zara Ali",
                   "Hina Ali",
                   "Nuha Ali",
                   "Sara Ali",
};
int i = 0;
for ( i = 0; i < MAX; i++)
{
   printf("Value of names[%d] = %s\n", i, names[i] );//字符型指针数组不用*names[i]
}

区分于数组指针:(一个地址),该指针指向数组,但指向的数组占多少个字节不确定,长度固定(32 位系统下占 4 个字节)。

5、多级指针:C 允许指向指针的指针。

二级指针存一级指针的地址,即二级指针指向一级指针。

三级指针存二级指针的地址,即三级指针指向二级指针。

6、传递指针给函数:通过引用或地址传递参数,使传递的参数在调用函数中被改变。

7、从函数返回指针(地址)

返回静态局部变量地址:

由于函数中的局部变量存储在内存的栈区,当函数调用结束后,局部变量所占的内存地址便被释放了。

而static 变量的值存放在内存中的静态数据区,不会随着函数执行的结束而被清除,故能返回其地址。

声明:

int * getRandom()

二、函数指针

函数指针:指向函数的指针。

1、声明

int (*fun_ptr)(int,int); //声明一个指向同样参数、返回值的函数指针类型

2、定义

fun_ptr=fun;

3、调用:可直接用指针调用。

fun_ptr(a,b);

例:

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

三、回调函数

1、回调函数就是一个通过函数指针调用的函数。

函数指针作为某个函数的参数:不同功能的函数有相同的形式,将需要调用的函数指针作为参数传递给一个函数,在函数内使用指针调用对应函数。

回调函数最大的意义在于解耦,降低代码之间的耦合度。

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()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。

2、回调函数实例(转载,以后可能需要)

一个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_WAIT_CTC, M26_WAIT_CTC  },  //等待调度中心回复 
    {GPRS_NETWORK_LINK_FEM, M26_LINK_FEM  }, //连接前置机
    {GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM  }, //等待前置机回复
    {GPRS_NETWORK_COMM,  M26_COMM   }, //正常工作    
    {GPRS_NETWORK_WAIT_Sig,  M26_WAIT_Sig  },  //等待信号回复
    {GPRS_NETWORK_GetSignal,  M26_GetSignal  }, //获取信号值
    {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上面的框架,只需要修改回调函数内部的具体实现,或者增加、减少回调函数,就可以很简洁快速的实现模块联网。

参考链接:C语言回调函数详解(全网最全)-CSDN博客

  • 49
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
函数指针回调函数C语言中非常重要的概念,它们在实际编程中经常被用到。在本文中,我将对函数指针回调函数进行详细的解释,并通过生动的例子进行说明。 一、函数指针 函数指针是指向函数的指针变量。它可以像普通指针一样存储一个函数的地址,并且可以通过该指针调用该函数。在C语言中,函数名本身就是一个指向函数的指针,所以可以将函数名直接赋值给一个函数指针变量。 下面是一个简单的例子: ```c #include <stdio.h> int add(int a, int b) { return a + b; } int main() { int (*p)(int, int) = add; int result = p(1, 2); printf("%d\n", result); return 0; } ``` 在这个例子中,我们定义了一个函数add,它接受两个整数参数并返回它们的和。然后我们定义了一个函数指针变量p,它指向add函数。最后我们通过p指针调用add函数,并将结果打印出来。 函数指针的好处在于可以将函数作为参数传递给其他函数,这就是回调函数的应用场景。 二、回调函数 回调函数是指作为参数传递给另一个函数的函数。当该函数需要某些特定的操作时,它会调用传递进来的回调函数来完成这些操作。回调函数通常用于事件处理、异步编程等场合。 下面是一个例子: ```c #include <stdio.h> void print(int num) { printf("%d\n", num); } void traversal(int *arr, int size, void (*callback)(int)) { int i; for (i = 0; i < size; ++i) { callback(arr[i]); } } int main() { int arr[] = {1, 2, 3, 4, 5}; traversal(arr, 5, print); return 0; } ``` 在这个例子中,我们定义了一个print函数,它接受一个整数参数并将其打印出来。然后我们定义了一个traversal函数,它接受一个整数数组、数组长度以及一个回调函数作为参数。在traversal函数中,我们遍历整个数组,并对每个元素调用回调函数。最后我们在main函数中调用traversal函数,并将print函数作为回调函数传递进去。 这个例子展示了回调函数的一个常见应用场景:遍历数组或链表时,需要对每个元素执行相同的操作,但是具体的操作可以由调用者自己定义。 三、函数指针回调函数的结合应用 函数指针回调函数经常被结合使用。下面是一个例子: ```c #include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } void calculate(int a, int b, int (*callback)(int, int)) { int result = callback(a, b); printf("%d\n", result); } int main() { calculate(1, 2, add); calculate(3, 4, sub); return 0; } ``` 在这个例子中,我们定义了两个函数add和sub,它们分别实现了加法和减法运算。然后我们定义了一个calculate函数,它接受两个整数参数和一个回调函数作为参数。在calculate函数中,我们调用传递进来的回调函数,完成相应的运算。最后我们在main函数中调用calculate函数,分别传递add和sub函数作为回调函数。 这个例子展示了如何通过函数指针回调函数实现不同的运算操作,从而提高代码的灵活性和可复用性。 综上所述,函数指针回调函数C语言中非常重要的概念,它们可以让我们通过代码来动态地指定函数的行为。在实际编程中,我们经常会使用到这两个概念,因此深入理解它们的原理和应用场景是非常有必要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值