c语言进阶部分详解(经典回调函数qsort()详解及模拟实现)

简介: c语言进阶部分详解(经典回调函数qsort()详解及模拟实现)

大家好!上篇文章我已经对回调函数进行了初步的讲解和一个简单的使用事例,鉴于篇幅有限没有进行更加详细的解释,今天便来补上。


一.回调函数的含义

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


二.qsort()函数

1.讲解

根据cplusplus网址给出的:

翻译这就来了:

qsort函数是C语言标准库中的一个函数,用于对数组进行快速排序。它的完整声明如下:

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));qsort函数接受四个参数:

  1. base:指向要排序数组的首元素的指针。
  2. nmemb:表示数组中元素的个数。
  3. size:表示每个元素的大小(以字节为单位)。
  4. compar:指向一个用于比较两个元素的回调函数的指针

回调函数compar用于比较两个元素的大小关系。它接受两个参数,分别是指向要比较的元素的指针。回调函数应该返回一个整数值,表示两个元素的大小关系。如果返回负数,则表示第一个元素小于第二个元素;如果返回正数,则表示第一个元素大于第二个元素;如果返回零,则表示两个元素相等。


一般这个回调函数是需要我们自己来写的:

//升序排序  针对整型的排序:
int compare (const void * a, const void * b)
 {
     return ( *(int*)a - *(int*)b );
 }
//降序排列
 int compare (const void * a, const void * b)
 {
     return ( *(int*)b - *(int*)a );
 }


2.实例

对整型数组排序

int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}
int main() {
    int arr[] = {5, 3, 8, 2, 1, 4};
    int size = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, size, sizeof(int), compare);
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

8f4d96cbb0ea479b904f56ce19f2199e.png

同时我们也可以对其他数据类型进行排序,下面便是对结构体进行排序

 struct Student{
    char name[20];
    int score;
} ;
int compare(const void* a, const void* b) {
    return ((struct Student*)a)->score - ((struct Student*)b)->score;
}
int main() {
   struct Student students[] = {
        {"Alice", 85},
        {"Bob", 92},
        {"Charlie", 78},
        {"David", 80},
        {"Eva", 88}
    };
    int size = sizeof(students) / sizeof(students[0]);
    qsort(students, size, sizeof(struct Student), compare);
    printf("按照成绩排序后的学生列表:\n");
    for (int i = 0; i < size; i++) {
        printf("姓名:%s,成绩:%d\n", students[i].name, students[i].score);
    }
    return 0;
}



三.利用冒泡排序来模拟qsort()

1.main函数

这里main只是进行了最为基本的一些处理,接下来进入bubble_qsort()函数

int main()
{
  int arr[] = { 10,9,8,7,6,5,4,3,2,1 };     //定义整型数组并初始化
  int sz = sizeof(arr) / sizeof(arr[0]);    //计算数组长度
  int i = 0;
  bubble_sort(arr, sz, sizeof(arr[0]), cmp);    //模拟qsort函数实现冒泡排序
  for (i = 0; i < sz; i++)                   
  {
    printf("%d ", arr[i]);                     //排序完后对数组进行打印,验证排序是否成功
  }
}1. int maiint main()
{
  int arr[] = { 10,9,8,7,6,5,4,3,2,1 };     //定义整型数组并初始化
  int sz = sizeof(arr) / sizeof(arr[0]);    //计算数组长度
  int i = 0;
  bubble_sort(arr, sz, sizeof(arr[0]), cmp);    //模拟qsort函数实现冒泡排序
  for (i = 0; i < sz; i++)                   
  {
    printf("%d ", arr[i]);                     //排序完后对数组进行打印,验证排序是否成功
  }
}


2.bubble_qsort()

冒泡排序函数bubble_sort,它接受四个参数:要排序的数组arr、数组的长度sz、每个元素的大小width和比较函数cmp。冒泡排序函数使用两层循环来实现冒泡排序的过程。外层循环控制冒泡排序的趟数,内层循环遍历每一趟需要比较的元素对。在每一趟冒泡排序中,如果比较函数返回的结果大于0,则交换相邻的两个元素,将较大的元素向后移动

  • 我们可以看到形参的类型是 void* arr ,此类型可接受任何类型指针
  • 我们会把需要比较的参数传递给比较函数cmp():

第一个是:(char*)arr + (j * width)  我们先把void*强转为char*,再加上j*width,width是每个元素的大小,j*width就是需要加上的字节数,所以(char*)arr + (j * width)就是第j个元素的第一个字节的地址

void bubble_sort(void* arr, int sz, int width, int(*cmp)(void* e1, void* e2))
{
  int i = 0;
  int j = 0;
  for (i = 0; i < sz - 1; i++)
  {
    //冒泡排序趟数
    for (j = 0; j < sz - 1 - i; j++)   //每一趟冒泡排序
    {
      if (cmp((char*)arr + (j * width), (char*)arr + (j + 1) * width)>0)
      {
        //符合条件进行交换
        swap((char*)arr + (j * width), (char*)arr + (j + 1) * width,width);
      }
    }
  }
}

3.cmp()

虽然传递过来的是char*类型的指针但是我们经过强制转换之后访问的还是四个字节

int cmp(void* e1, void* e2)   //所选择的比较方法
{
  return *((int*)e1) - *((int*)e2);
}

4.swap()

函数的参数包括两个指针p1p2,分别指向需要交换的两个元素,以及一个整数width,表示每个元素的大小。


在函数内部,我们使用一个临时变量t来保存交换过程中的临时值。然后使用一个循环,遍历每个字节,将两个元素逐个字节地交换位置

void swap(char* p1, char* p2, int width)   //实现数组元素的交换
{
  int t = 0;
  int i = 0;
  for (i = 0; i < width; i++)
  {
    t = *p1;
    *p1 = *p2;
    *p2 = t;
    p1++;
    p2++;
  }
}


原码:

#include<stdio.h>
//仿qsort函数重写冒泡排序
int cmp(void* e1, void* e2)   //所选择的比较方法
{
  return *((int*)e1) - *((int*)e2);
}
void swap(char* p1, char* p2, int width)   //实现数组元素的交换
{
  int t = 0;
  int i = 0;
  for (i = 0; i < width; i++)
  {
    t = *p1;
    *p1 = *p2;
    *p2 = t;
    p1++;
    p2++;
  }
}
void bubble_sort(void* arr, int sz, int width, int(*cmp)(void* e1, void* e2))
{
  int i = 0;
  int j = 0;
  for (i = 0; i < sz - 1; i++)
  {
    //冒泡排序趟数
    for (j = 0; j < sz - 1 - i; j++)   //每一趟冒泡排序
    {
      if (cmp((char*)arr + (j * width), (char*)arr + (j + 1) * width)>0)
      {
        //符合条件进行交换
        swap((char*)arr + (j * width), (char*)arr + (j + 1) * width,width);
      }
    }
  }
}
int main()
{
  int arr[] = { 10,9,8,7,6,5,4,3,2,1 };     //定义整型数组并初始化
  int sz = sizeof(arr) / sizeof(arr[0]);    //计算数组长度
  int i = 0;
  bubble_sort(arr, sz, sizeof(arr[0]), cmp);    //模拟qsort函数实现冒泡排序
  for (i = 0; i < sz; i++)                   
  {
    printf("%d ", arr[i]);                     //排序完后对数组进行打印,验证排序是否成功
  }
}

当然,此模拟方法依然有很多缺点:

  1. 冒泡排序虽然简单,但是效率低
  2. 逐个字节地交换位置适用于任意类型的元素,不受元素类型和大小的限制。但是,它的缺点是效率较低


以后希望我在学到新知识后能进行相应改善。


目录
相关文章
|
4天前
|
存储 编译器 Linux
c语言进阶(2)
c语言进阶(2)
28 0
|
4天前
|
C语言
【进阶C语言】数组笔试题解析
【进阶C语言】数组笔试题解析
23 0
|
4天前
|
存储 程序员 C语言
【进阶C语言】C语言文件操作
【进阶C语言】C语言文件操作
43 0
|
4天前
|
存储 C语言
C语言进阶---------作业复习
C语言进阶---------作业复习
|
4天前
|
存储 Linux C语言
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-2
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
4天前
|
自然语言处理 Linux 编译器
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-1
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
4天前
|
存储 编译器 C语言
C语言进阶第十课 --------文件的操作-1
C语言进阶第十课 --------文件的操作
|
4天前
|
存储 程序员 C语言
C语言进阶第九课 --------动态内存管理-2
C语言进阶第九课 --------动态内存管理
|
4天前
|
编译器 C语言
C语言进阶第九课 --------动态内存管理-1
C语言进阶第九课 --------动态内存管理
|
4天前
|
C语言
C语言进阶第八课 --------通讯录的实现
C语言进阶第八课 --------通讯录的实现