C语言:深入理解指针(5)

目录

一、回调函数

[二、qsort 使用举例](#二、qsort 使用举例)

三、模拟qsort


一、回调函数

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

举个例子:

cs 复制代码
int Add(int x, int y)
{
   return x+y;
}

void test(int (*pf)(int, int))
{
   int r = pf(10 ,20);
   printf("%d\n" ,r);
}

int main()
{
   test(Add);
   return 0;
}

简单点说,就是把函数的地址作为一个参数传递给另外一个函数,在其中被调用,那么被调用的函数就称为回调函数

二、qsort 使用举例

qsort 是C语言中提供的一个排序函数,是基于快速排序思想的。

它的原型如下:

cs 复制代码
void qsort(void* base, size_t num, size_t size, int(*compar)(const void* ,const void*));

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

size_t num //元素个数

size_t size //元素的大小(单位:字节)

int (*compar)(const void* ,const void*) //函数指针,指向的函数是用来比较被排序数组中的两个元素,根据自己需要,自己编写

下面举个例子:

cs 复制代码
int compar(const void* p1, const void* p2)
{
   if( *(int*)p1 > *(int*)p2 )
      return 1;
   else if( *(int*)p1 < *(int*)p2)
      return -1;
   else
      return 0;
}

这个自行编写的函数要满足一下要求:

(1)返回类型为整型

(2)在默认升序的情况下,当 p1 > p2时,返回1, p1 == p2 时返回 0 ,p1 < p2 时返回 -1。

(3)如果你想降序,把(2)中的 条件互换就行

(4)具体的比较标准由你自己来定,你可以比较整形,浮点型,字符型或者其他,不过要记得强制类型转换

三、用冒泡排序模拟 qsort 函数

首先,要模拟,我们需要根据 qsort 函数的原型模拟:

cs 复制代码
void my_bubble( void* base, size_t num ,size_t size, int (*compare)(const void* ,const void*);

然后我们在把原本普通的冒泡函数写出来:

cs 复制代码
​
void my_bubble( void* base, size_t num ,size_t size, int (*compare)(const void* ,const void*)
{
   for(int i = 0 ;i <sz-1;i++)
   {
      for(int j = 0 ;j <sz-1-i ;j++)
      {
         if(arr[j] > arr[j+1])//判断条件我们需要修改
         {
            //交换过程我们也要修改
            int mid = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = mid;
         }
   }
}

​

为什么要用void*呢?因为我们不知道要排序的数据类型

为什么要专门写一个比较函数呢?因为我们不提前知道比较规则

那如何比较两个元素呢?我们连数据类型都不知道

如图所示,我们知道起始地址,但不知道元素类型,也就不知道一个元素究竟占多少字节,整型一个元素占4个字节,短整型占两个字节,我们该如何准确地找到下一个元素的地址呢?

如图,别忘了我们还有每个元素的大小,那么第 j 个元素的地址就是 arr+j*size ,j 第 +1 个 元素的地址就是 arr + (j +1)*size 。

但是,void*作为无类型指针,是不可以被计算和解引用的,那我们就要对其强制类型转化为 char* 类型。

为什么是 char* 类型呢?因为char的大小为 1 个字节,是最小的单位了,你总不能拿 int 去吧,你用了int ,那char 、short 怎么办呢

所以那个条件应该为:

cs 复制代码
if(compar((char*)p1 ,(char*)p2) > 0)

那如果满足这个条件了,按照原先冒泡函数思想,我们就应该互换位置了,那怎么互换呢?

互换一般要创建第三个变量,但是,我们不知道元素类型,就不知道第三者变量的类型,怎么办呢?

我们每个字节每个字节互换:

如此往复循环,直到两个元素调换完成,那我们应该循环几次呢?答案是 size 次,因为每个元素就是 size 个字节,每次调换一个字节,需要调换 size 次

cs 复制代码
void Swap(char* su1, char*su2 ,size_t size)
{
   for(int i = 0; i<sz ;i++)
   {
      char mid = *su1;
      *su1 = *su2;
      *su2 = mid;
      su1++;
      su2++;
   }
}

好了,我们的用冒泡排序模拟qsort就可以出来了:

cs 复制代码
​void Swap(char* su1, char*su2 ,size_t size)
{
   for(int i = 0; i<sz ;i++)
   {
      char mid = *su1;
      *su1 = *su2;
      *su2 = mid;
      su1++;
      su2++;
   }
}


void my_bubble( void* base, size_t num ,size_t size, int (*compare)(const void* ,const void*)
{
   for(int i = 0 ;i <sz-1;i++)
   {
      for(int j = 0 ;j <sz-1-i ;j++)
      {
         if(compare((char*)p1+j*size , (char*)p2+(j+1)*size) >0)
         {
            swap((char*)p1+j*size , (char*)p2+(j+1)*size) ,size);
         }
   }
}
相关推荐
i***13244 分钟前
SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由
java·spring cloud·gateway
计算机徐师兄6 分钟前
Java基于微信小程序的食堂线上预约点餐系统【附源码、文档说明】
java·微信小程序·食堂线上预约点餐系统小程序·食堂线上预约点餐微信小程序·java食堂线上预约点餐小程序·食堂线上预约点餐小程序·食堂线上预约点餐系统微信小程序
无心水1 小时前
【分布式利器:腾讯TSF】10、TSF故障排查与架构评审实战:Java架构师从救火到防火的生产哲学
java·人工智能·分布式·架构·限流·分布式利器·腾讯tsf
燃于AC之乐3 小时前
我的算法修炼之路--4 ———我和算法的爱恨情仇
算法·前缀和·贪心算法·背包问题·洛谷
Boilermaker19928 小时前
[Java 并发编程] Synchronized 锁升级
java·开发语言
Cherry的跨界思维8 小时前
28、AI测试环境搭建与全栈工具实战:从本地到云平台的完整指南
java·人工智能·vue3·ai测试·ai全栈·测试全栈·ai测试全栈
MM_MS9 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
独自破碎E9 小时前
【二分法】寻找峰值
算法
alonewolf_999 小时前
JDK17新特性全面解析:从语法革新到模块化革命
java·开发语言·jvm·jdk
一嘴一个橘子9 小时前
spring-aop 的 基础使用(啥是增强类、切点、切面)- 2
java