学习C语言(十三)——深入理解指针(4)

目录

1.回调函数

 2.qsort使用举例

3.qsort函数的模拟实现

3.1 冒泡排序

 3.2 使用冒泡排序模拟qsort函数


1.回调函数

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

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

        在学习C语言(十二)——深入理解指针(3)的第六点中,我们讲解了转移表的三个版本:有大量重复冗余的版本、去掉重复冗余的版本、使用函数指针数组的版本。第二种版本就是使用了回调函数。

        下面右边的代码就是吧调用的函数的地址以参数的形式传递到另一个函数中,使用函数指针接收,函数指向什么函数就调用什么函数。

 2.qsort使用举例

        qsort的头文件是 <stdlib.h>,下面是qsort函数的输入格式以及一些概念

void qsort(void* base,		// base指向待排序的第一个元素
			size_t num,		// 待排序的元素个数
			size_t size,	// 待排序的数组元素的大小,单位是字节
			int (*compar)(const void*, const void*)		
			// compar是一个函数指针,指向的函数能够比较两个元素
			);
// qsort函数有4个参数,可以对任意类型的数据进行排序,使用的是快速排序的思想进行排序的
// qsort函数是库函数,可以直接使用
//void*是一种指针类型

         假设有一个整形乱序数组,使用qsort来对这个数组进行排序。示例如下:

        strcmp 是库函数,是用来比较两个字符的大小的,按照英文字母的顺序,对相同位置的字符进行排序。例如 zhangsan 、lisi、la,l 在英文字母中排序靠前,再对 lisi 和 la的第二个字符进行排序,a在英文字母中排序靠前,最后的顺序就为 la、lisi、zhangsan。

        接下来,如果有一个结构体student,使用这个结构体创建一个数组stu,将这个数组stu进行排序。对结构体中的name进行排序。示例如下:

         对结构体中的age进行排序。示例如下:

        对结构体的一些成员访问方式

//结构体类型
struct student
{
	char name[20];
	int age;
};
int main()
{
	struct student stu = {"zhangsan",20};
	printf("%s %d\n", stu.name, stu.age);//结构体成员访问操作:.	结构体.成员名

	struct student* ps = &stu;
	printf("%s %d\n", (*ps).name, (*ps).age);
	printf("%s %d\n", ps->name, ps->age);//结构体成员访问操作:->	结构体->成员名
	return 0;
}

        关于更多的结构体的知识不在此赘述,这里主要是展示 qsort 函数能够对各种类型的数据进行排序。

3.qsort函数的模拟实现

3.1 冒泡排序

        使用回调函数,采用冒泡排序的方式来模拟实现qsort函数。

        首先,让我们来回忆一下冒泡函数的。冒泡排序进行升序排序,是将一个整型数组中i个元素,两两相邻的元素进行比较,比较完之后将两个元素的下标加一往后推,这样比较了i-1次 之后,就会将最大的元素放到最后,每排完一趟之后,就可以在下一趟少进行一次排序(因为最大的已经排序到了后面),最后就可以将数组中的元素进行升序排序。

        冒泡排序的思想如下图:

        冒泡排序的代码,如下所示:

 3.2 使用冒泡排序模拟qsort函数

        qsort 函数是库函数,可以排序任意类型的数据。根据之前的介绍,在qsort 函数中使用void*类型来接收数据以确保能够接收任意类型的数据。

        在上面的 bubble_sort 函数中,我们可以对那些进行保留?那些需要更改呢?

        在 bubble_sort 这个函数中,使用第一个for循环来表示冒泡循环的趟数,第二个for循环来表示每一趟冒泡排序的排序过程。在冒泡排序的思想中,这两个for循环符合冒泡循环的思想,即便接收的数据类型从整型变成了其他的数据类型,也不会产生影响,所以不需要更改。

        需要更改的有下面这些方面:

  1. 改造参数——让这个函数能够接受任意类型的数据
  2. 改造比较方法——让函数能欧在排序时,能够比较不同类型的数据
  3. 交换的代码——上面的代码只是对整型的元素进行交换,要让它可以对任意类型的数据进行交换

        最后更改的到的使用冒泡排序的方法模拟qsort函数的代码,如下所示:

#include <stdio.h>
#include <string.h>

//bubble_sort模拟qsort函数
void swap(char* buf1, char* buf2,int size)			//对符合条件的元素进行交换
{
	for (int i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, int sz,int size,int (*cmp)(void* ,void* ))			//使用冒泡排序的方法模拟qsort函数
{
	// base 用于接受数组的首元素		count 是数组中元素的个数
	// size 是数组中每个元素的大小		(*cmp)(void*,void*)是一个函数指针,用于比较两个元素的大小 
	//冒泡排序进行的趟数
	for (int i = 0; i < sz; i++)
	{
		//每一趟冒泡排序排序的过程
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
				//接受到的base是void*类型的,所以不能直接进行加减。转换成char*后,可以接收任意类型的数据
				// size是单个元素的大小,j*size和(j+1)*size是计算在base的基础上加几个字节可以得到下一个进行比较的元素
				//将两个元素进行交换
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
		}
	}
}

//测试bubble_sort排序结构体整形类型数据
int cmp(const void* e1, const void* e2)				//对整型数组的元素进行比较
{
	return (*(int*)e1)-(*(int*)e2);
}

void pri_arr(int arr[], int sz)				//对整型数组进行打印
{
	for (int i = 0; i < sz; i++)
		printf("%d ", arr[i]);
	printf("\n");
}
//测试bubble_sort排序整型数据
void test1()
{
	int arr[] = { 3,5,6,7,9,1,2,4,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("排序之前:");
	pri_arr(arr, sz);
	bubble_sort(arr, sz,sizeof(arr[0]),cmp);
	printf("\n排序之后:");
	pri_arr(arr, sz);
}
//测试bubble_sort排序结构体类型数据
struct student
{
	char name[20];
	int age;
};
int cmp_name(const void* e1, const void* e2)		//对结构体子成员name进行比较
{
	return strcmp(((struct student*)e1)->name, ((struct student*)e2)->name);
}
int cmp_age(const void* e1, const void* e2)			//对结构体子成员age进行比较
{
	return (((struct student*)e1)->age)-(((struct student*)e2)->age);
}
void pri_struct(struct student* s, int sz)			//将结构体进行打印
{
	for (int i = 0; i < sz; i++)
		printf("%s %d \n", ((struct student*)s + i)->name, ((struct student*)s + i)->age);
}
void test2()
{
	struct student s[] = {{"zhangsan",20},{"lisi",15},{"wangwu",23}};
	int sz = sizeof(s) / sizeof(s[0]);
	printf("排序之前:\n");
	pri_struct(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_name);		//对结构体的子成员name的数据进行排序
	//bubble_sort(s, sz, sizeof(s[0]), cmp_age);			//对结构体的子成员age的数据进行排序
	printf("\n排序之后:\n");
	pri_struct(s, sz);
}

//测试bubble_sort排序字符串类型数据
void pri_ch(char ch[], int str)
{
	for (int i = 0; i <str;i++)
		printf("%c ", ch[i]);
}
int cmp_ch(const void* e1, const void* e2)
{
	return strcmp((char*)e1, (char*)e2);
}
void test3()
{
	char ch[] = "cdafbeg";
	int str = strlen(ch);
	printf("排序前:");
	pri_ch(ch, str);
	bubble_sort(ch, str, sizeof(ch[0]), cmp_ch);
	printf("\n排序后:");
	pri_ch(ch, str);
}

int main()
{
	//test1();			//测试bubble_sort排序整型数据
	test2();			//测试bubble_sort排序结构体类型数据
    test3();            //测试bubble_sort排序字符串类型数据
	return 0;
}

        将整型数组进行排序,测试如下:

        将结构体按照子成员name进行排序,测试如下:

         将结构体按照子成员age进行排序,测试如下:

         将字符串数组进行排序,测试如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值