目录
一.回调函数:
1.回调函数的定义:
2.回调函数的使用:
3.qsort函数的使用:
4.利用回调函数模拟实现qsort函数:
二.总结:
博客主页:张栩睿的博客主页
欢迎关注:点赞+收藏+留言
系列专栏:c语言学习
家人们写博客真的很花时间的,你们的点赞和关注对我真的很重要,希望各位路过的朋友们能多多点赞并关注我,我会随时互关的,欢迎你们的私信提问,也期待你们的转发!
希望大家关注我,你们将会看到更多精彩的内容!!!
一.回调函数:
1.回调函数的定义:
我们都知道,函数的调用除了我们最基础的调用方式之外,通过函数指针我们也可以实现对函数的调用。函数指针调用函数的函数,就称为回调函数。
如果你把函数的地址作为参数(即使用指针)传递给另一个函数 ,且当这个指针调用了它指向的函数时,我们就把这样的使用方式称作回调函数。
2.回调函数的使用:
实例1:
  
   - 
    
     
    
    
     
      //函数1:
     
    
- 
    
     
    
    
     
      //函数1使用了这样的调用方式,就被称为回调函数
     
    
- 
    
     
    
    
     
      void 
      test(
      )
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      printf(
      "test\n");
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      //函数2:
     
    
- 
    
     
    
    
     
      //使用函数指针接收,并对函数test进行调用:
     
    
- 
    
     
    
    
     
      void 
      TEST(
      void(*p)())
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      //使用函数指指针调用test函数:
     
    
- 
    
     
    
    
     	
      p();
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     	
      printf(
      "TEST\n");
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      int 
      main(
      )
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      //将函数地址传递给另一个函数:
     
    
- 
    
     
    
    
     	
      TEST(test);
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     	
      return 
      0;
     
    
- 
    
     
    
    
     
      }
     
    
 在这个过程中,函数 test 就被作为函数参数传递了出去,并且在函数 TEST 中被函数指针接收,且该指针调用了它指向的 test 函数,于是我们就可以说 test 函数是回调函数。
实例2:
我们之前通过转移表简化了计算器代码的书写计算器的书写,我们是否可以用回调函数简化呢?
  
   - 
    
     
    
    
     
      void menu()
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      printf(
      "*******************************\n");
     
    
- 
    
     
    
    
     	
      printf(
      "****** 1. add 2. sub *****\n");
     
    
- 
    
     
    
    
     	
      printf(
      "****** 3. mul 4. div *****\n");
     
    
- 
    
     
    
    
     	
      printf(
      "****** 0. exit *****\n");
     
    
- 
    
     
    
    
     	
      printf(
      "*******************************\n");
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      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;
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      void calc(int (*pf)(int, int))
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      int x = 
      0;
     
    
- 
    
     
    
    
     	
      int y = 
      0;
     
    
- 
    
     
    
    
     	
      int ret = 
      0;
     
    
- 
    
     
    
    
     	
      printf(
      "请输入两个操作数:>");
     
    
- 
    
     
    
    
     	
      scanf(
      "%d %d", &x, &y);
     
    
- 
    
     
    
    
     
      	ret = 
      pf(x, y);
     
    
- 
    
     
    
    
     	
      printf(
      "%d\n", ret);
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      int main()
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      int input = 
      0;
     
    
- 
    
     
    
    
     	
     
    
- 
    
     
    
    
     	
      do 
     
    
- 
    
     
    
    
     
      	{
     
    
- 
    
     
    
    
     		
      menu();
     
    
- 
    
     
    
    
     		
      printf(
      "请选择:>");
     
    
- 
    
     
    
    
     		
      scanf(
      "%d", &input);
     
    
- 
    
     
    
    
     		
      switch (input)
     
    
- 
    
     
    
    
     
      		{
     
    
- 
    
     
    
    
     		
      case 
      1:
     
    
- 
    
     
    
    
     			
      calc(Add);
     
    
- 
    
     
    
    
     			
      break;
     
    
- 
    
     
    
    
     		
      case 
      2:
     
    
- 
    
     
    
    
     			
      calc(Sub);
     
    
- 
    
     
    
    
     			
      break;
     
    
- 
    
     
    
    
     		
      case 
      3:
     
    
- 
    
     
    
    
     			
      calc(Mul);
     
    
- 
    
     
    
    
     			
      break;
     
    
- 
    
     
    
    
     		
      case 
      4:
     
    
- 
    
     
    
    
     			
      calc(Div);
     
    
- 
    
     
    
    
     			
      break;
     
    
- 
    
     
    
    
     		
      case 
      0:
     
    
- 
    
     
    
    
     			
      printf(
      "退出计算器\n");
     
    
- 
    
     
    
    
     			
      break;
     
    
- 
    
     
    
    
     		
      default:
     
    
- 
    
     
    
    
     			
      printf(
      "选择错误\n");
     
    
- 
    
     
    
    
     			
      break;
     
    
- 
    
     
    
    
     
      		}
     
    
- 
    
     
    
    
     
      	} 
      while (input);
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     	
      return 
      0;
     
    
- 
    
     
    
    
     
      }
     
    
 3.qsort函数的使用:
我们之前学过使用冒泡排序,用冒泡排序写一个函数来对整形数组进行升序降序,但是局限就是只能对整形,对其他类型无法排序。而qsort函数对于任意类型都可以排序,整形,字符型,甚至结构体型都可以,那么我们该如何·使用·qsotr函数呢?
库函数 qsort 是基于快速排序法实现的一个排序函数。
我们在cplusplus上看看用法:

函数括号里到底传入了什么参数呢?

这里的compare函数如何书写呢?

不难发现,在升序排序中,compare是将两个要排序的元素的地址作为参数,将他们比较大小的差作为返回值。
如果结果>0,返回正数
如果结果=0.返回0
如果结果<0,返回负数
那么就会有同学问了,那么降序怎么办?
如果降序的话就让两个值交换位置相减即可。
那么就会有同学问了,那个void*是什么类型,不同类型如何转化?


下面我们就来看看结构体的排序吧!
  
   - 
    
     
    
    
     
      struct Stu
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      char name[
      20];
     
    
- 
    
     
    
    
     	
      int age;
     
    
- 
    
     
    
    
     
      };
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      //按照学生的年龄来排序
     
    
- 
    
     
    
    
     
      int cmp_stu_by_age(const void* e1, const void* e2)
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      return ((
      struct Stu*)e1)->age - ((
      struct Stu*)e2)->age;
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      //按学生的姓名排序
     
    
- 
    
     
    
    
     
      int cmp_stu_by_name(const void* e1, const void* e2)
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      return strcmp(((
      struct Stu*)e1)->name, ((
      struct Stu*)e2)->name);
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      void test2()
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      struct Stu s[
      3] = { {
      "zhangsan",
      20}, {
      "lisi", 
      50}, {
      "wangwu", 
      33} };
     
    
- 
    
     
    
    
     	
      int sz = 
      sizeof(s) / 
      sizeof(s[
      0]);
     
    
- 
    
     
    
    
     	
      //qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
     
    
- 
    
     
    
    
     
      	qsort(s, sz, 
      sizeof(s[
      0]), cmp_stu_by_name);
     
    
- 
    
     
    
    
     
      }
     
    
 这里需要注意的是strcmp的使用,他对于两个字符串的每个字符一一对比,如果大于返回正数,等于就往后继续比,小于就返回负数。这和我们要返回的数值正好对应。
下面,我们就通过冒泡排序来实现qsort函数吧!
4.利用回调函数模拟实现qsort函数:
首先我们回忆一下冒泡排序:
  
   - 
    
     
    
    
     
      #include <stdio.h>
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      void bubble_sort(int arr[],int sz)
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
         
      int i = 
      0;
     
    
- 
    
     
    
    
         
      for (i = 
      0; i < sz - 
      1; i++)
     
    
- 
    
     
    
    
     
          {
     
    
- 
    
     
    
    
             
      int j = 
      0;
     
    
- 
    
     
    
    
             
      for (j = 
      0; j < sz - i - 
      1; j++)
     
    
- 
    
     
    
    
     
              {
     
    
- 
    
     
    
    
                 
      if (arr[j] > arr[j + 
      1])
     
    
- 
    
     
    
    
     
                  {
     
    
- 
    
     
    
    
                     
      int tmp = arr[j];
     
    
- 
    
     
    
    
     
                      arr[j] = arr[j + 
      1];
     
    
- 
    
     
    
    
     
                      arr[j + 
      1] = tmp;
     
    
- 
    
     
    
    
     
                  }
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
     
          }
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      int main()
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
         
      int arr[] = { 
      1,
      2,
      3,
      4,
      0,
      8,
      9,
      7,
      6,
      5 };
     
    
- 
    
     
    
    
         
      int sz = 
      sizeof(arr) / 
      sizeof(arr[
      0]);
     
    
- 
    
     
    
    
     
          bubble_sort(arr, sz);
     
    
- 
    
     
    
    
         
      int i = 
      0;
     
    
- 
    
     
    
    
         
      for (i = 
      0; i < sz; i++)
     
    
- 
    
     
    
    
     
          {
     
    
- 
    
     
    
    
     
              printf(
      "%d ", arr[i]);
     
    
- 
    
     
    
    
     
          }
     
    
- 
    
     
    
    
         
      return 
      0;
     
    
- 
    
     
    
    
     
      }
     
    
 这样的冒泡排序已经写死了整形,不能比较其他类型。
我们设计一个兼容可以排序整型,又可以排字符型和结构体的呢
模拟qsort的思想,利用冒泡的形式
  
   - 
    
     
    
    
     
      void swap(char* buf1, char* buf2, int width)
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      int i = 
      0;
     
    
- 
    
     
    
    
     	
      for (i = 
      0; i < width; i++)
     
    
- 
    
     
    
    
     
      	{
     
    
- 
    
     
    
    
     		
      char tmp = *buf1;
     
    
- 
    
     
    
    
     
      		*buf1 = *buf2;
     
    
- 
    
     
    
    
     
      		*buf2 = tmp;
     
    
- 
    
     
    
    
     
      		buf1++;
     
    
- 
    
     
    
    
     
      		buf2++;
     
    
- 
    
     
    
    
     
      	}
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
     
      int cmp_int(void* e1, void* e2)
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      return *(
      int*)e1 - *(
      int*)e2;
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
     
      void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      int i = 
      0, j = 
      0;
     
    
- 
    
     
    
    
     	
      for (i = 
      0; i < sz - 
      1; i++)
     
    
- 
    
     
    
    
     
      	{
     
    
- 
    
     
    
    
     		
      for (j = 
      0; j < sz - 
      1 - i; j++)
     
    
- 
    
     
    
    
     
      		{
     
    
- 
    
     
    
    
     			
      if (cmp((
      char*)
      base + j * width, (
      char*)
      base + (j + 
      1) * width) > 
      0)
     
    
- 
    
     
    
    
     
      			{
     
    
- 
    
     
    
    
     
      				swap((
      char*)
      base + j * width, (
      char*)
      base + (j + 
      1) * width,width);
     
    
- 
    
     
    
    
     
      			}
     
    
- 
    
     
    
    
     
      		}
     
    
- 
    
     
    
    
     
      	}
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
     
      int main()
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      //整数:
     
    
- 
    
     
    
    
     	
      int arr[
      10] = { 
      9,
      8,
      7,
      6,
      5,
      4,
      3,
      2,
      1,
      0 };
     
    
- 
    
     
    
    
     	
      int sz = 
      sizeof(arr) / 
      sizeof(arr[
      0]);
     
    
- 
    
     
    
    
     
      	bubble_sort(arr, sz, 
      sizeof(arr[
      0]), cmp_int);
     
    
- 
    
     
    
    
     	
      for (
      int i = 
      0; i < sz; i++)
     
    
- 
    
     
    
    
     
      		printf(
      "%d ",arr[i]);
     
    
- 
    
     
    
    
     
      }
     
    
 这里有两处非常重要的点:
首先是这个冒泡排序:
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
因为我们不知道我们会对什么类型的变量排序,所以我们用char*强转指针,使这个指针的步长为一,后面就可以显现出width(类型大小)的作用了:
因为我们不知道类型,不能用[]下标访问传过来的数组,就只能用指针的形式,通过指针+-整数来访问这个数组的每个元素。我们根据+width的倍数,正好就是这个类型的步长。
其次是这个swap交换函数:
void swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
由于我们不知道要交换元素的类型,所以我们通过变量中拥有最小单位:一个字节的char类型来依次交换每个字节来实现元素的交换,这样是不是很简便呢?
 
 
我们在来看一看结构体类型的交换:

  
   - 
    
     
    
    
     
      void swap(char* buf1, char* buf2, int width)
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      for (
      int i = 
      0; i < width; i++)
     
    
- 
    
     
    
    
     
      	{
     
    
- 
    
     
    
    
     		
      char tmp = *buf1;
     
    
- 
    
     
    
    
     
      		*buf1 = *buf2;
     
    
- 
    
     
    
    
     
      		*buf2 = tmp;
     
    
- 
    
     
    
    
     
      		buf1++;
     
    
- 
    
     
    
    
     
      		buf2++;
     
    
- 
    
     
    
    
     
      	}
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
     
      struct stu
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      char name[
      20];
     
    
- 
    
     
    
    
     	
      int age;
     
    
- 
    
     
    
    
     
      };
     
    
- 
    
     
    
    
     
      int cmp_stu_by_name(void* e1, void* e2)
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      return (strcmp(((
      struct stu*)e1)->name, ((
      struct stu*)e2)->name));
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
     
      void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(void* e1, void* e2))//这里等下看一下
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      int i, j;
     
    
- 
    
     
    
    
     	
      for (i = 
      0; i < sz; i++)
     
    
- 
    
     
    
    
     
      	{
     
    
- 
    
     
    
    
     		
      for (j = 
      0; j < sz - 
      1 - i; j++)
     
    
- 
    
     
    
    
     
      		{
     
    
- 
    
     
    
    
     			
      if (cmp((
      char*)
      base + j * width, (
      char*)
      base + (j + 
      1) * width) > 
      0)
     
    
- 
    
     
    
    
     
      			{
     
    
- 
    
     
    
    
     
      				swap((
      char*)
      base + j * width, (
      char*)
      base + (j + 
      1) * width,width);
     
    
- 
    
     
    
    
     
      			}
     
    
- 
    
     
    
    
     
      		}
     
    
- 
    
     
    
    
     
      	}
     
    
- 
    
     
    
    
     
      }
     
    
- 
    
     
    
    
     
      int main()
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
     	
      //结构体排序
     
    
- 
    
     
    
    
     	
      struct stu s[
      3] = { {
      "zhangsan",
      20}, {
      "lisi", 
      50}, {
      "wangwu", 
      33} };
     
    
- 
    
     
    
    
     	
      int sz = 
      sizeof(s) / 
      sizeof(s[
      0]);
     
    
- 
    
     
    
    
     	
      //bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
     
    
- 
    
     
    
    
     
      	bubble_sort(s, sz, 
      sizeof(s[
      0]), cmp_stu_by_name);
     
    
- 
    
     
    
    
     
      }
     
    
 结果:

不难发现,bubble_sort函数,swap函数都一模一样,都是写好了的,所以我们只要自己写出那个回调函数cmp函数(比较大小) 即可,这就是qsort的方便之处。
二.总结:
通过qsort函数的学习,我们对回调函数有了更深的理解。这里我们在复习一下qsort里的参数。


今天我们算是给我们的指针升级之路画上了一个完美的句号,至此我们关于指针的进阶的学习就告一段落了。 辛苦各位小伙伴们动动小手,三连走一波 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!
转载:https://blog.csdn.net/qq_74310471/article/details/128597957
 
					