深度理解C指针(下):回调函数和利用冒牌排序的底层逻辑模拟qsort的实现

简介: 深度理解C指针(下):回调函数和利用冒牌排序的底层逻辑模拟qsort的实现

@[TOC]

回调函数

首先定义一下什么是回调函数:

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

那么如何进行理解回调函数?借助一个实际的例子来看

#include <stdio.h>
#include <stdlib.h>
int int_cmp(const void* p1, const void* p2)
{
   
    return (*(int *)p1 - *(int *)p2);
}

int main()
{
   
    int arr[10] = {
    2,1,3,4,5,6,7,8,9,10 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), int_cmp);
    for (int i = 0; i < sz; i++)
    {
   
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

借助这个例子,我们再来看回调函数的概念,回调函数就是一个通过函数指针调用的函数,在这个例子中,qsort函数内部的int_cmp就是一个函数指针,当代码运转到该语句时,就会通过这个函数指针调用这个函数,这是在特定的情况下(语句执行到该语句)才会被另一方调用的(被这里的qsort调用),用于对于该事件进行响应。


这里用到了void的概念那么什么是void

void*是一种什么样的指针?

void 是一种通用指针,它可以接收所有类型的地址,当你不知道该用什么类型的指针来接收一个地址时,可以选择void

那它岂不是万能的了?

事实上,它也有它的弊端,当你把这个地址存起来,想要再次调用时,void*并不能直接调用,因为它并不知道存储的是几个字节的数据,因此在调用时需要强制类型转换,通过这个转换过程后,就可以把它取出来了

利用冒泡排序的底层逻辑模拟实现qsort函数

qsort函数的功能是很强大的,只需要给它提供需要排序的首地址,元素个数和每个元素所占内存空间的大小,再给他提供一个比较函数的函数指针,它就能实现数组的升序或降序排序,那么这个函数是怎么进行的,我们该如何进行模拟实现?


在c语言中,qsort是借助快速排序的思想进行排序的,在这里我们采用一下冒泡排序的思想,通过冒泡排序的思想进行模拟实现

分析思路

首先我们要自己写qsort函数,第一步要确定函数参数是什么
qsort函数的参数分别是:要排序数组的首元素地址;排序的元素个数;每一个元素所占字节的大小;一个函数指针

那么基本逻辑就确定下来了

void bubble_sort(void* base,size_t num,size_t width,int (*cmp)(const void * p1,const void* p2))

回忆一下冒泡排序,我们在冒泡排序中的思想是两两元素进行比大小,比大小后如果符合if语句就进行交换,然后进行下一轮的比较,那么在这个函数体中该如何实现?

我们可以嵌套一个函数swap函数,用来进行交换

那么下一个问题是,我们并不知道传参过来的是什么类型的数据,如何表示要交换的两个数?

解决方案是,char占用的字节是一个字节,因此可以利用这个条件,把默认要交换的数的地址当成char*,但是它们的下一个元素的地址表示的时候采用的是这个元素地址+元素类型的大小,这样就可以模拟实现各种类型的数据了

例如,我们要排序的是1和3,我们让首元素地址转换为char,那么比较函数和交换函数的第一个参数的char指向的是1的地址,第二个参数可以用第一个参数的地址+4(int类型数据所占大小为4),这样这个地址就指向了3的起始点,然后我们让这两个地址之间的每一个字节进行交换,这样就达到了全部交换的目的

那么函数体目前就写为

void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* p1, const void* p2))
{
   
    for (size_t i = 0; i < num - 1; i++)
    {
   
        for (size_t j = 0; j < num - 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);
            }
        }
    }
}

Swap函数我们前面提到了,只要让它每一个字节进行交换,就能让这两个地址的内容交换

那么Swap函数实现如下:

void Swap(char* buf1,char* buf2,int width)
{
   
    int i = 0;
    for (int i = 0; i < width; i++)
    {
   
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

那么整体就实现完成了:

#include <stdio.h>

void Swap(char* buf1,char* buf2,int width)
{
   
    int i = 0;
    for (int i = 0; i < width; i++)
    {
   
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* p1, const void* p2))
{
   
    for (size_t i = 0; i < num - 1; i++)
    {
   
        for (size_t j = 0; j < num - 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 int_cmp(const void* p1, const void* p2)
{
   
    return (*(int *)p1 - *(int *)p2);
}

int main()
{
   
    int arr[10] = {
    2,1,3,4,5,6,7,8,9,10 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz, sizeof(arr[0]), int_cmp);
    for (int i = 0; i < sz; i++)
    {
   
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

这样写的函数可以用来处理所有类型的数据,float/double/char/int/甚至结构体都能进行相应的排序,功能还是很强大的

相关文章
|
1月前
|
C++
指针中的回调函数与qsort的深度理解与模拟
本文详细介绍了回调函数的概念及其在计算器简化中的应用,以及C++标准库函数qsort的原理和使用示例,包括冒泡排序的模拟实现。
15 1
|
1月前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
21 2
|
1月前
魔法指针 之 函数指针 回调函数
魔法指针 之 函数指针 回调函数
12 0
|
5月前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
6月前
|
算法
指针(6)---qsort函数
指针(6)---qsort函数
35 0
|
6月前
指针(5)---回调函数
指针(5)---回调函数
29 0
|
6月前
|
编译器 C语言
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。(下)
C语言进阶⑪(指针上)(知识点和对应练习)回调函数模拟实现qsort。
42 0
|
5月前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
6月前
|
算法 搜索推荐 C语言
c函数指针与回调函数
c函数指针与回调函数
44 2
|
1月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
22 0