【C语言】深入理解指针(四)qsort函数的实现

打嗝小狗~ 2024-09-10 08:05:02 阅读 67

指针4

1.回调函数是什么2.qsort使用举例3.qsort函数的模拟实现

1.回调函数是什么

回调函数就是⼀个通过函数指针调⽤的函数。

如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数

时,被调⽤的函数就是回调函数。

回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。

上一篇中我们写的计算机的实现的代码中,代码是重复出现的,其中虽然执⾏计算的逻辑是区别的,但是输⼊输出操作是冗余的,有没有办法,简化⼀些呢?

<code>int add(int a, int b)

{

return a + b;

}

int sub(int a, int b)

{

return a - b;

}

int mul(int a, int b)

{

return a * b;

}

int div(int a, int b)

{

return a / b;

}

int main()

{

int x, y;

int input = 1;

int ret = 0;

do

{

printf("*********************\n");

printf(" 1:add 2:sub \n");

printf(" 3:mul 4:div \n");

printf("*********************\n");

printf("请选择:");

scanf("%d", &input);

switch (input)

{

case 1:

printf("输⼊操作数:");

scanf("%d %d", &x, &y);

ret = add(x, y);

printf("ret = %d\n", ret);

break;

case 2:

printf("输⼊操作数:");

scanf("%d %d", &x, &y);

ret = sub(x, y);

printf("ret = %d\n",ret);

break;

case 3:

printf("输⼊操作数:");

scanf("%d %d", &x, &y);

ret = mul(x, y);

printf("ret = %d\n", ret);

break;

case 4:

printf("输⼊操作数:");

scanf("%d %d", &x, &y);

ret = div(x, y);

printf("ret = %d\n", ret);

break;

case 0:

printf("退出程序\n");

break;

default:

printf("选择错误\n");

break;

}

} while (input);

return 0;

}

我们发现调用的函数都是int (int,int)类型的,我们可以把调⽤的函数的地址以参数的形式传递过去,使⽤这样类型的函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函数的功能。

int add(int a, int b)

{

return a + b;

}

int sub(int a, int b)

{

return a - b;

}

int mul(int a, int b)

{

return a * b;

}

int div(int a, int b)

{

return a / b;

}

void calc(int(*pf)(int, int))

{

int ret = 0;

int x, y;

printf("输⼊操作数:");

scanf("%d %d", &x, &y);

ret = pf(x, y);

printf("ret = %d\n", ret);

}

int main()

{

int x, y;

int input = 1;

int ret = 0;

do

{

printf("*********************\n");

printf(" 1:add 2:sub \n");

printf(" 3:mul 4:div \n");

printf("*********************\n");

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;

}

因为上方代码,只有调⽤函数的逻辑是有差异的,我们可以把调⽤的函数的地址以参数的形式

传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函

数的功能

注意区分这和我们在【C语言篇】深入理解指针3(附转移表源码)中实现的转移表,这里使用的是回调函数,但在转移表中我们使用的是函数指针数组

2.qsort使用举例

默认升序

在这里插入图片描述

quick sort快速排序的思想,冒牌排序是拍整形数据,这个是对任意类型的排序,

在这里插入图片描述

1.指向待排序数组第一个元素的指针

2.数组元素个数

3.数组中一个元素的大小,单位是字节

4.函数指针 指向函数的地址,指向比较2个元素的函数的指针,它指向的是一个需要两个无类型指针作为参数的函数

在这里插入图片描述

qsort函数排序整型数据

<code>#include <stdio.h>

#include <stdlib.h>

//qosrt函数的使⽤者得实现⼀个⽐较函数

int int_cmp(const void * p1, const void * p2)

{

return (*( int *)p1 - *(int *) p2);//p1 p2是名字

}

int main()

{

int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };

int i = 0;

qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);

//sizeof(int)=sizeof(arr[0])

for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)

{

printf( "%d ", arr[i]);

}

printf("\n");

return 0;

}

使用qsort排序结构数据

struct Stu //学⽣

{

char name[20];//名字

int age;//年龄

};

//假设按照年龄来⽐较 e1e2一交换就是降序

int cmp_stu_by_age(const void* e1, const void* e2)

{

//强转为结构体指针,找成员,整数比较做差

return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;

}

//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的,比较对应位置上字符的ASCII的大小

//假设按照名字来⽐较

int cmp_stu_by_name(const void* e1, const void* e2)

{

return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);

}

//按照年龄来排序

void test2()

{

struct Stu s[] = { { "zhangsan", 20}, { "lisi", 30}, { "wangwu", 15} };

int sz = sizeof(s) / sizeof(s[0]);

qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);

}

//按照名字来排序

void test3()

{

struct Stu s[] = { { "zhangsan", 20}, { "lisi", 30}, { "wangwu", 15} };

int sz = sizeof(s) / sizeof(s[0]);

qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);

}

int main()

{

test2();

test3();

return 0;

}

3.qsort函数的模拟实现

qsost底层采用的是快速排序的方法,在这里我们使用更简单的冒泡排序的排序算法来模拟实现qsort函数,这里使用void*的指针,以实现泛型编程,换句话说,使⽤回调函数,模拟实现qsort(采⽤冒泡的⽅式)。

#include <stdio.h>

int int_cmp(const void * p1, const void * p2)

{

return (*( int *)p1 - *(int *) p2);

}

void _swap(void *p1, void * p2, int size)

{

int i = 0;

for (i = 0; i< size; i++)

{

//一个一个字节交换

char tmp = *((char *)p1 + i);

*(( char *)p1 + i) = *((char *) p2 + i);

*(( char *)p2 + i) = tmp;

}

}

void bubble(void *base, int count , int size, int(*cmp )(void *, void *))

{

int i = 0;

int j = 0;

for (i = 0; i< count - 1; i++)//趟数

{

for (j = 0; j<count-i-1; j++)//趟数内部比较

{

//都是base,所以强转,size是宽度单位字节,arr首元素的地址传给base

//if后面的(是到最后size的

if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)

{

_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);

}

}

}

}

int main()

{

int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };

int i = 0;

bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);//函数名

for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)

{

printf( "%d ", arr[i]);

}

printf("\n");

return 0;

}

在这里插入图片描述

qsort的实现思路推荐博客



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。