函数指针和回调函数

函数指针和回调函数

        我们知道,在C语言中定义一个变量就要开辟相应的空间,通常都在栈上存放。那么我们的函数呢?乃至我们的整个程序呢?这就要说到虚拟地址空间了。

虚拟地址空间

        那么什么时虚拟地址空间呢?虚拟地址空间就像是一个沙箱,每个程序都单独的在各自的沙箱中运行,在我们的32位操作系统下每个程序的虚拟地址空间是4GB,那么就会有人说了,我的电脑就只有4GB的内存,咋能跑既能听歌又能写代呢?因为整是虚拟地址空间,他是要经过页表进行内存映射才会映射到我们真正的物理内存中,而在映射的过程中,只映射有数据的部分,所以说起来是4GB但是在物理内存上就可能占用个不到100M,可以先这样理解,到后面我们可以进行深入讨论。

        下面给出我们虚拟内存空间的大体布局图:

                                          

        大家看到没,在我们的虚拟内存空间中,地址较为低的部分就是我们的代码区,我们程序就是在这里存储,所以说我们的代码在运行时也是会被加载到内存里,既然在内存中,那么我们的代码也是有地址的。

函数指针

        上面已经说过,我们的代码是在内存中存储的,代码也是有地址的,那么我们的函数也是代码,那么我们可以对函数取地址吗?答案是肯定的。

        我们可以写一段小程序来测试一下:

  #include <stdio.h>
  ​
  int Fun1(){
      
  }
  ​
  int Fun2(){
      
  }
  ​
  int main(){
      int aa = 0;
      int bb = 0;
      printf("&aa = %p\n",&aa);
      printf("&bb = %p\n",&bb);
      printf("&Fun1 = %p\n", Fun1);       //注意这里没有取地址符
      printf("&Fun2 = %p\n", &Fun2);
      return 0;
  }

        运行结果为:

  &aa = 0x7ffe87521f80
  &bb = 0x7ffe87521f84
  &Fun1 = 0x55e8090476aa
  &Fun2 = 0x55e8090476b1

        是不是Fun1和Fun2的地址比变量aa和bb地址低了好多呢?是不是从侧边也印证了我们上面的图呢?也同时印证了我们的函数是可以取地址的,既然能够取地址,那么这个地址就可以被存起来,就成为了我们的函数指针。

        我们的函数名就是函数的地址,对我们的函数名取地址也是函数的地址,那么我们该用那个呢?当然是那和简单用那个了。

        函数指针就是指向函数的指针那么我们如何去定义呢?

 int (*p)(int , int );

        先是返回值类型,然后就是我们的指针名,最后就是指向函数的参数类型;如何理解呢,星号先和p结合形成一个指针所以就是一个指针变量,后面的是参数类型,最前面是指向函数的返回值类型。

        那么这个如何理解呢?

  int *p(int , int );

        这个有木有很像我们定义函数呢?其实这就是一个返回值的 int* 参数为int int的函数,所以我们在用的时候一定要小心。

        那么在日常写代码中怎么用呢?

        我们可以看一下这个简易的计算器:

#include <stdio.h>

int add(int x , int y){
    return x + y;
}

int sub(int x , int y){
	return x - y;
}

int mul(int x ,int y){
    return x * y;
}

int div(int x ,int y){
    return x / y;
}

int main(){
	int select , op1 , op2 ;
	int result = 0;
    int (*arr[5])(int x, int y) = {0 , add , sub , mul , div};	//函数指针数组
    printf(" 请输入你的选项 :\n");
    printf("**** 1 . 加法   2. 减法 ****\n");
    printf("**** 3 . 加法   4. 减法 ****\n");
    scanf("%d", &select);
    printf("请输入两个操作数: ");
    scanf("%d %d", &op1 , &op2);
    result =  (*arr[select])(op1, op2);
    printf("结果是 : %d\n", result);
    return 0;
}

        如果用一般的方法是不是要一堆的if else,或者使用switch ,但是用函数指针的方式就简便了好多呢?

回调函数

        我们的函数指针是不是一个指针变量?那么它能不能作为一个函数的参数呢?答案时是肯定的。在我们的操作系统源码,以及在linux中许多系统调用中,有许多这样的函数。

        回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当 这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调 用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

        我们C语言库函数中就有一个典型的案例:qsort 函数

  void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

        该函数的作用是对所给定的缓冲区的元素进行排序,元素个数为num ,元素大小为width ,最后一个是一个 函数指针,指向你提供的一个比较函数。

        对于 void* 类型的理解 , void* 是一个空指针类型 ,理论上可以接收任何指针,但是不能够直接使用,需要强转后才可以使用。

        我们可以用qsort来对一个整型数组来进行排序:

  #include <stdio.h>
  #include <stdlib.h>
  ​
  int cmp_int(const void *x ,const  void *y){
      int *_x_ = (int *)x;
      int *_y_ = (int *)y;
      
      if(*_x_ > *_y_){
          return 1;
      }else if(*_x_ > *_y_){
          return -1;
      }else{
          return 0;
      }
  }
  ​
  void PrintArr(const int *Arr , int len){
      int i = 0;
      for( ; i<len ; i++){
          printf("%d ", Arr[i]);
      }
      printf("\n");
  }
  ​
  int main(){
      int Arr[10] = {11,22,55,66,44,22,33,77,88,11};
      printf("Before Qsort:\n");
      PrintArr(Arr,sizeof(Arr)/sizeof(Arr[0]));
      qsort(Arr, 10 ,4, cmp_int);
      printf("After Qsort:\n");
      PrintArr(Arr,sizeof(Arr)/sizeof(Arr[0]));
      exit(0);    
  }

        这样我们就利用库函数qsort实现了对一个数组的排序,是不是很简单呢,只需要按照要求写一个判断两个数字大小的函数传入到我们的qsort中就可以了。

总结

        以上就是我们今天所讨论的函数指针和回调函数,这哪里只是让大家认识一下这两个东西,以后大家在遇到或者是在用的时候不至于手忙脚乱,希望对大家有所帮助。

 

        由于本人才疏学浅,若有疏忽还望不吝赐教。

        @YeLing0119

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
函数指针是指向函数指针变量。在C语言中,函数指针可以用来存储函数的地址,并且可以通过函数指针来调用该函数函数指针可以作为参数传递给其他函数,使得其他函数可以通过该指针来调用指向的函数,这就是回调函数的实现方式之一。 回调函数是通过函数指针调用的函数。当我们把一个函数指针(地址)作为参数传递给另一个函数,并且在该函数内部通过该指针来调用指向的函数时,我们就称这个被调用的函数回调函数回调函数不是由该函数的实现方直接调用的,而是由其他函数在特定的条件下调用的。这种机制可以提高程序的灵活性和可扩展性。 在C语言中,我们可以使用函数指针来实现回调函数。具体来说,我们可以将函数指针传递给另一个函数,然后这个函数就可以通过这个指针来调用回调函数了。这种方式可以使得回调函数在被调用时具有更大的灵活性,因为回调函数可以根据需要被多次调用,也可以在不同的上下文中被调用。 此外,函数指针还可以用来创建函数指针数组。函数指针数组是一个数组,其中的元素都是函数指针。可以使用数组下标来访问和调用数组中的不同函数指针,从而实现对不同函数的调用。这种方式可以便于管理和组织多个函数指针,使得程序的结构更加清晰和易于维护。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值