【指针的进阶(3)】回调函数和qsort排序各种类型的数据

简介: 【指针的进阶(3)】回调函数和qsort排序各种类型的数据

前言

前两章讲了指针的类型,数组传参和指针传参,还有函数指针和函数指针数组,接下来第三章讲回调函数


指针函数非常大的用途就是实现回调函数

一、回调函数是什么?

回调函数就是通过函数指针调用的函数。

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

如何实现回调函数

下面的代码过于冗余

如果分装一个函数,调用它,能大大减少了敲代码工作量,这个函数就是回调函数

所以分装一个函数叫Calc,在使用加减乘除时调用这个函数

case 1:
      Calc(Add);
      break;
    case 2:
      Calc(Sub);
      break;
    case 3:
      Calc(Mul);
      break;
    case 4:
      Calc(Div);
      break;

函数地址传给Calc函数,用函数指针接收

这个pf是函数指针,指向的参数是(int, int),返回类型是int

void Calc(int (*pf)(int, int))

具体代码如下

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 menu()
{
  printf("***************************\n");
  printf("*****  1.add  2.sub  ******\n");
  printf("*****  3.mul  4.div  ******\n");
  printf("*****  0.exit        ******\n");
  printf("***************************\n");
}
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("ret = %d\n", ret);
}
int main()
{
  int input = 0;
  int x = 0;
  int y = 0;
  int ret = 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;
}

过程如下:

不是直接调用Add函数,而是把Add函数传给pf指针函数,,通过pf函数指针去调用Add函数,实现计算,那么pf函数指针就是回调函数 。

作用: 回调函数更具有广泛性和通用性,代码不易写死,如果直接调用Add函数,代码就被固定住了,要想调用别的函数,那个代码就不适用了。

二、回调函数的应用——qsort

qsort 是标准库里的函数,用来排序

qsort函数怎么实现回调函数呢?

这就得说到冒泡排序了

一般冒泡排序是这样写的:

int main()
{
  int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz - 1; i++)
  {
    int j = 0;
    for (j = 0; j < sz - 1 - i; j++)
    {
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
}

这如果要排结构体,浮点型等其他类型的数据呢,这个代码就存在一定的问题了,只适用于整型的排序,形式固定住了,不灵活不广泛。

那有没有一种写一个排序函数,适用于任何类型呢?

qsort函数就能解决这个问题

qsort排序各种类型的数据

qsort函数的特点

1.快速排序的方法

2.适合于任意类型数据的排序

  1. 在cplusplus网站上搜索 qsort,可以看到qsort有四个参数:

四个函数的意思是:

void qsort(

void* base,//指向需要排序的数组的第一个元素

size_t num,//排序的元素个数

size_t size,//一个元素的大小,单位是字节

int(*cmp)(const void*, const void*)

);//函数指针类型-这个函数指针指向的函数,能够比较base指向数组中的两个元素


用qsort排序整型

根据上面四个参数,写出下面这段代码

  1. 最后一个参数空出来没有写,是因为这里涉及到一些重要的知识点
    第四个参数是要写一个函数,能够比较base指向数组中的两个元素,并把结果返回。 那么就写一个函数叫cmp_int,参数就是cplusplus网站上qsort函数给的两个参数
    代码如下:
int cmp_int(const void* p1, const void* p2)
  1. 写完了函数,怎么比较两个数呢?
    在cplusplus网站上找到计算方法如下:

    当p1指向的值小于p2指向的值时,返回小于0的数字;
    当p1指向的值等于p2指向的值时,返回0;
    当p1指向的值大于p2指向的值时,返回大于0的数字;

那么就让p1p2两个数作差,把结果返回去。

但这里还涉及到一些知识:

一个热知识::void* 的指针是无具体类型的指针。
void* 类型的指针可以接收任意类型的地址

在函数参数这里用void* 的好处就是,广泛性,什么类型的数据排序都能接收,编程不会报警告。

但这种类型的指针不能直接解引用,也不能直接进行指针运算。所以在比较两个数大小时,需要强制类型转换。

意思就是要排序什么类型,就强制类型转换什么类型

比如:当要比较整型数据时,p1和p2要强制类型转换成整型,当比较浮点型数据时,p1和p2强制类型转换成浮点型。

代码如下:

int cmp_int(const void* p1, const void* p2)
{
  return (*(int*)p1 - *(int*)p2);
}
  1. 最后把结果打印出来
    分装一个print函数
    参数是整型数组和数组大小
    循环打印每个元素
void print(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
}

完整代码如下:

#include<stdlib.h>
int cmp_int(const void* p1, const void* p2)
{
  return (*(int*)p1 - *(int*)p2);
}
void print(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
}
test1()
{
  int arr[10] = {9,8,7,6,5,4,3,2,1,0};
  int sz = sizeof(arr) / sizeof(arr[0]);
  qsort(arr,sz,sizeof(arr[0]),cmp_int);
  print(arr, sz);
}
int main()
{
  test1();
  return 0;
}

注意:qsort 函数的头文件是 #include<stdlib.h>


用qsort 排序结构体

年龄比较大小

#include<stdio.h>
#include<stdlib.h>
struct Stu
{
  char name[20];
  int age;
};
int cmp_stu_by_age(const void* p1, const void* p2)
{
  return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
void print(struct Stu arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i].age);
  }
}
void test2()
{
  struct Stu arr[] = { {"zhangsan",20},{"lisi",50},{"wangwu",15} };
  int sz = sizeof(arr) / sizeof(arr[0]);
  qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
  print(arr,sz);
}
int main()
{
  test2();
  return 0;
}

姓名比较大小

#include<stdio.h>
#include<stdlib.h>
struct Stu
{
  char name[20];
  int age;
};
int cmp_str_stu_by_name(const void* p1, const void* p2)
{
  return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
//名字比较不能相减,名字是字符串,字符串比较大小用strcmp
void print(struct Stu arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%s ", arr[i].name);
  }
}
void test3()
{
  struct Stu arr[] = { {"zhangsan",50},{"lisi",15},{"wangwu",30} };
  int sz = sizeof(arr) / sizeof(arr[0]);
  qsort(arr, sz, sizeof(arr[0]), cmp_str_stu_by_name);
  print(arr, sz);
}

总结

本章讲了回调函数的含义如何实现回调函数qsort排序各种类型的数据的内容,希望对您有帮助!

相关文章
|
6月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
2月前
|
C++
指针中的回调函数与qsort的深度理解与模拟
本文详细介绍了回调函数的概念及其在计算器简化中的应用,以及C++标准库函数qsort的原理和使用示例,包括冒泡排序的模拟实现。
25 1
|
2月前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
28 2
|
2月前
|
C语言 C++
【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)
【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)
|
2月前
魔法指针 之 函数指针 回调函数
魔法指针 之 函数指针 回调函数
21 0
|
3月前
|
存储 Go
Go: struct 结构体类型和指针【学习笔记记录】
本文是Go语言中struct结构体类型和指针的学习笔记,包括结构体的定义、成员访问、使用匿名字段,以及指针变量的声明使用、指针数组定义使用和函数传参修改值的方法。
|
4月前
|
存储 安全 Go
深入理解 Go 语言中的指针类型
【8月更文挑战第31天】
52 0
|
6月前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
6月前
|
存储 C语言 C++
指针进阶(函数指针)(C语言)
指针进阶(函数指针)(C语言)
|
6月前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)