深入理解回调函数qsort:从入门到模拟实现(上)

简介: 深入理解回调函数qsort:从入门到模拟实现(上)

🍁博客主页:江池俊的博客

💫收录专栏:C语言进阶之路

💡代码仓库:江池俊的代码仓库

🎪我的社区:GeekHub

🎉欢迎大家点赞👍评论📝收藏⭐


前言



回调函数qsortC语言编程中重要的概念,它们为我们提供了强大的工具,用于处理函数指针和数组排序。本篇博客将逐步介绍回调函数的概念,详细解释 qsort 函数的用法,并通过一个模拟实现,帮助初学者更好地理解这些概念。如果大家不知道函数指针是说明或还不清楚函数指针的内容,可以移步我这篇文章《掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!》


一、什么是回调函数?



回调函数是一种通过函数指针传递给其他函数,并由其他函数在适当时候调用的函数。回调函数的存在使得我们能够将某种特定的行为(代码逻辑)作为参数传递给另一个函数。这在编程中非常有用,因为它允许我们以灵活的方式自定义函数的行为。


📌使用回调函数的优势


  1. 代码重用: 可以将通用的操作封装在回调函数中,以供多个函数重复使用。
  2. 灵活性: 回调函数允许我们在运行时动态地指定要执行的代码,从而实现更高度的灵活性。
  3. 解耦合: 使用回调函数可以将代码分解成独立的模块,减少模块之间的耦合,提高代码的可维护性。


二、qsort 函数及其用法



qsort 是 C 标准库中提供的用于数组排序的函数,它接受一个 比较函数 作为参数,用于确定数组元素的顺序。(这个比较函数是使用者根据自己的需要设计的,因此qsort函数可以实现对任意类型数据的排序)qsort 函数的原型如下:


void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));


其中,base 是要排序的数组的指针;num 是数组中元素的数量;size 是每个元素的大小,以字节为单位;compar 是用于比较两个元素的函数指针。

这里我们可以通过cplusplus网来查询这个函数的使用方法,也可以使用菜鸟教程网来查询。


📌qsort函数作用


4d088427875648a19325f62fe1ec43a3.png


📌qsort函数4个参数的介绍


cf899b3bd11c4ee885e8c9d60055d7af.png

📌为什么qsort函数的参数是这四个?


qsort 函数之所以有这四个参数,是为了实现通用、灵活且可定制的排序功能。

这些参数的设计和使用有以下几个目的:


  1. 通用性 由于 qsort 需要适应不同类型的数据,它通过 base 参数接受数组的指针,并使用 size 参数来了解每个元素的大小,从而使得排序操作可以应用于各种不同类型的数组。
  2. 灵活性: 通过传递比较函数的指针作为 compar 参数,我们可以在不同的排序场景中定义不同的比较逻辑。这使得我们可以根据需要实现升序、降序或自定义的排序规则。
    可定制性:
    qsort 的设计允许我们在排序过程中自定义元素的比较方式。我们可以根据实际需求提供不同的比较函数,从而实现不同的排序需求。
    高效性:
    qsort 内部使用一种高效的排序算法(通常是快速排序的变种),以确保在大多数情况下能够高效地完成排序操作。


综上所述,这四个参数的设计使得 qsort 函数成为一个强大且通用的排序工具,可以适应不同类型的数据、实现不同的排序规则,并且在实际应用中能够高效地完成排序任务。


📌第4个参数—>compar比较函数的剖析


cf899b3bd11c4ee885e8c9d60055d7af.png


qsort 函数中,要实现升序或降序排序,这需要根据比较函数的逻辑来确定元素的顺序。比较函数的返回值将决定元素的排列方式。


  • 如果比较函数返回负值,qsort 将认为第一个元素应该在第二个元素之前,从而实现升序排序。
  • 如果比较函数返回正值,qsort 将认为第一个元素应该在第二个元素之后,从而实现降序排序。
  • 如果比较函数返回零,qsort 将认为两个元素相等,它们的顺序将是未定义的。

注意:


  • qsort 函数的比较函数参数是两个 void 类型的指针,是为了提高灵活性和通用性。这样的设计允许您在不同的排序场景中使用同一个 qsort 函数,无论排序的数据类型是什么。
  • 当编写一个通用的排序函数时,我们无法预先知道要排序的数据类型是什么。因此,将比较函数的参数声明为 void 类型的指针,使得 qsort 函数可以接受任何类型的数据


由此,我们可以得到qsort函数的使用模板如下:

#include <stdio.h>
#include <stdlib.h>
// 定义一个的数据类型(示例:整数类型)
typedef int MyType;
// 比较函数
int compareMyType(const void *a, const void *b) {
    return (*(MyType *)a - *(MyType *)b);//实现升序
    //return (*(MyType *)b - *(MyType *)a);//实现降序
}
int main() {
    int numElements = ...; // 数组中元素的数量
    MyType arr[numElements]; // 声明并初始化一个数组
    // 使用 qsort 对数组进行排序
    qsort(arr, numElements, sizeof(MyType), compareMyType);
    // 打印排序后的数组
    for (int i = 0; i < numElements; i++) {
        printf("%d ", arr[i]); // 打印数组元素
    }
    return 0;
}


三、qsort函数实例



注意:以下统一以升序为例


📌排序int类型数组


代码展示:

#include<stdio.h>
#include<stdlib.h>
//实现一个比较整型的函数
int compare_int(const void* a, const void* b)
{
  return *(int*)a - *(int*)b;//强制转换为int类型并解引用
}
//使用qsort对数组进行排序,升序
int main()
{
  int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  printf("排序前:");
  for (int i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  //排序
  qsort(arr,sz,sizeof(int),compare_int);
  //打印
  printf("\n排序后:");
  for (int i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
  return 0;
}


运行结果:

044776eb60514e0ca75d210f8902d395.png


📌排序char类型数组


代码展示:

#include <stdio.h>
#include <stdlib.h>
int compare_char(const void* a, const void* b)
{
  return *(char*)a - *(char*)b; //强制转换为char类型并解引用
}
int main()
{
  char arr[] = { 'f', 'e','d','b','a','c' };
  int sz = sizeof(arr) / sizeof(arr[0]);
  printf("排序前:");
  for (int i = 0; i < sz; i++)
  {
    printf("%c ", arr[i]);
  }
  //排序
  qsort(arr, sz, sizeof(arr[0]), compare_char);
  //打印
  printf("\n排序后:");
  for (int i = 0; i < sz; i++)
  {
    printf("%c ", arr[i]);
  }
  printf("\n");
  return 0;
}


运行结果:

c6c715b117a249c0acc0dd9a185be687.png


📌排序浮点型数组


代码展示:

#include <stdio.h>
#include <stdlib.h>
int compare_float(const void* a, const void* b)
{
  float num1 = *(float*)a;
  float num2 = *(float*)b;
  if (num1 < num2) return -1;
  if (num1 > num2) return 1;
  return 0;
}
int main()
{
  float arr[] = { 5.2 , 2.5 , 3.14 , 1.5 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  printf("排序前:");
  for (int i = 0; i < sz; i++)
  {
    printf("%f ", arr[i]);
  }
  //排序
  qsort(arr, sz, sizeof(arr[0]), compare_float);
  //打印
  printf("\n排序后:");
  for (int i = 0; i < sz; i++)
  {
    printf("%f ", arr[i]);
  }
  printf("\n");
  return 0;
}


注意:


  • 由于浮点数的精度和范围有限,返回差值可能导致精度丢失和不稳定的结果,特别是在极端情况下。因此,在处理浮点数时,使用差值可能会引发一些问题。
  • 为了确保排序的稳定性和正确性,最好的做法是显式地使用 if 语句来比较元素的值,并返回 -1、0 或 1,以确保在各种情况下都能获得正确的比较结果。


运行结果:

c69012547ab14e288b736b70484b71fe.png


📌排序结构体类型数组


代码展示:

1. 【按姓名来排序】

//按姓名来排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student
{
  char name[20];
  int age;
}stu;
int compare_name(const void* a, const void* b)
{
  return strcmp( ((stu*)a)->name, ((stu*)b)->name );//比较字符大小使用strcmp函数
  //strcmp函数返回值与compare_name函数一致
}
int main()
{
  stu s[3] = { {"张三",20},{"李四",18},{"王五",25} };
  int sz = sizeof(s) / sizeof(s[0]);
  printf("排序前:");
  for (int i = 0; i < sz; i++)
  {
    printf("%s %d", s[i].name, s[i].age);
    if (i < sz - 1)
      printf(" | ");
  }
  //排序
  qsort(s, sz, sizeof(s[0]), compare_name);
  //打印
  printf("\n排序后:");
  for (int i = 0; i < sz; i++)
  {
    printf("%s %d", s[i].name, s[i].age);
    if (i < sz - 1)
      printf(" | ");
  }
  printf("\n");
  return 0;
}


运行结果:

96c342e1d4c64fe388c48793e5d9a90a.png


2. 【按年龄来排序】

代码展示:

//按年龄来排序
#include <stdio.h>
#include <stdlib.h>
typedef struct Student
{
  char name[20];
  int age;
}stu;
int compare_age(const void* a, const void* b)
{
  return (((stu*)a)->age - ((stu*)b)->age);
}
int main()
{
  stu s[3] = { {"张三",20},{"李四",18},{"王五",25} };
  int sz = sizeof(s) / sizeof(s[0]);
  printf("排序前:");
  for (int i = 0; i < sz; i++)
  {
    printf("%s %d", s[i].name, s[i].age);
    if (i < sz - 1)
      printf(" | ");
  }
  //排序
  qsort(s, sz, sizeof(s[0]), compare_age);
  //打印
  printf("\n排序后:");
  for (int i = 0; i < sz; i++)
  {
    printf("%s %d", s[i].name, s[i].age);
    if (i < sz - 1)
      printf(" | ");
  }
  printf("\n");
  return 0;
}


运行结果:

aea5d91c823b4f6c8d82de6e35f4cfff.png


目录
相关文章
|
2月前
|
搜索推荐 C语言
c语言从入门到实战——回调函数与qsort的讲解和模拟实现
回调函数是一个函数,它作为参数传递给另一个函数,并且能够在该函数内部被调用。在C语言中,回调函数通常被用于实现事件处理和排序算法中。 `qsort`是C标准库中的一个排序函数,它可以对任意类型的数组进行排序。`qsort`需要三个参数:要排序的数组、数组元素的个数和一个指向回调函数的指针。回调函数必须满足两个条件:能够比较数组中的元素,返回一个整数表示它们之间的大小关系;并且它应该能够被`qsort`函数调用。 回调函数是一种在编程中广泛使用的技术,它允许一个函数作为参数传递给另一个函数,并在需要时被调用。这种机制使得代码更加灵活和可重用。
37 0
|
2月前
|
C语言
c语言进阶部分详解(经典回调函数qsort()详解及模拟实现)
c语言进阶部分详解(经典回调函数qsort()详解及模拟实现)
45 0
|
2月前
|
编译器 C语言
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。(下)
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。
25 0
|
2月前
|
存储 C语言 C++
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。(上)
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。
17 0
|
2月前
|
存储 C语言
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。(中)
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。
27 0
|
2月前
回调函数,以qsort函数为例
回调函数,以qsort函数为例
16 0
|
7月前
|
程序员
qsort函数的模拟实现
qsort函数的模拟实现
32 1
|
9月前
|
算法 程序员 C语言
【进阶C语言】排序函数(qsort)与模拟实现(回调函数的实例)
回调函数为C语言重要知识点,以函数指针为主要知识;下面介绍回调函数的定义、回调函数的库函数举例即库函数模拟实现。
31 0
【进阶C语言】排序函数(qsort)与模拟实现(回调函数的实例)
|
7月前
回调函数与qsort函数
回调函数与qsort函数
22 0
|
9月前
|
存储 算法 测试技术
深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
24 0

热门文章

最新文章