c语言函数指针,基于函数回调模拟实现qsort函数,实测案例


前言

qsort函数C语言编译器函数库自带的排序函数。 qsort 的函数原型是void qsort (void*base,size_t num,size_t width,int (__cdecl*compare) (const void*,const void*)); 是base所指数组进行排序。 qsort函数包含在C 标准库 - <stdlib.h>中。

一、代码

这个是整体代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//qsort函数使用
//void qsort(void* base,//base中存放的是待排序数据中第一个对象的地址
//			size_t num,//排序元素中的个数
//			size_t size,//排序元素中一个元素所占字节大小
//			int (*cmp)(const void *e1,const void *e2)//函数指针用来比较两个元素的大小
//用户自定义比较函数如果第一个元素大于第二个元素返回值大于零,等于返回值等于零,小于返回值小于零
int cmp_int(const void* e1, const void* e2) {
	return *((int*)e1) - *((int*)e2);
}
//打印数组
void print(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test1() {
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);//库里排序函数
	print(arr, sz);
}
//交换函数
void Swap(char*buf1,char*buf2,int width) {
	int i = 0;
	for (i = 0; i < width; i++) {
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

//模拟qsort实现一个冒泡排序的通用算法
void bubble_sort(void* base,
	int sz,
	int width,
	int (*cmp)(const void*e1,const 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*)base+j*width, (char*)base + (j+1) * width)>0) {
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
//测试bubble_sort函数是否正确(整形比较)
void test2() {
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);//库里排序函数
	print(arr, sz);
}
//结构体
struct stu{
	char name[20];
	int age;

};
//结构体比较
//结构体比较函数
//通过年龄比较
int cmp_struct_age(const void* e1, const void* e2) {
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;

}
int cmp_struct_name(const void* e1, const void* e2) {
	return strcmp(((struct stu*)e1)->name , ((struct stu*)e2)->name);
}

//结构体比较测试函数qsort
void test3() {
	void print_stu(struct stu s[3], int sz);
	struct stu s[3] = { {"zhangsan",30} ,{"lisi",34}, {"wangwu",20} };
	int sz = sizeof(s) / sizeof(s[0]);
	print_stu(s, sz);
	qsort(s, sz, sizeof(s[0]), cmp_struct_name);
	print_stu(s, sz);
	qsort(s, sz, sizeof(s[0]), cmp_struct_age);
	print_stu(s, sz);

}
//打印结构体成员函数
void print_stu( struct stu *s ,int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("name=%s, age=%d", s[i].name, s[i].age);
		printf("\n");
	}
	printf("\n");
}
//结构体比较测试函数bubble_sort
void test4() {
	void print_stu(struct stu s[3], int sz);
	struct stu s[3] = { {"zhangsan",30} ,{"lisi",34}, {"wangwu",20} };
	int sz = sizeof(s) / sizeof(s[0]);
	print_stu(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_struct_name);
	print_stu(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_struct_age);
	print_stu(s, sz);

}


int main() {
	test1();//测试qsort排序函数,以及用户自定义cmp_int比较函数
	test2();//测试bubble_sort自定义排序函数cmp_int比较函数
	test3();//测试qsort排序函数,以及用户自定义比较结构体函数cmp_struct_name,cmp_struct_age
	test4();//测试bubble_sort排序函数,以及用户自定义比较结构体函数cmp_struct_name,cmp_struct_age
	return 0;
}

二、主要代码分析

我们这次主要是测试qsort函数,和通过函数指针模拟实现一个排序方式为冒泡排序的bubble_sort函数,并针对整形数组,和结构体进行排序.

测试函数的代码备注为测试代码的功能,这里不再进行解释.接下来让我们如何用函数回调进行模拟实现qsort函数的吧!

//qsort函数使用
//void qsort(void* base,//base中存放的是待排序数据中第一个对象的地址
//			size_t num,//排序元素中的个数
//			size_t size,//排序元素中一个元素所占字节大小
//			int (*cmp)(const void *e1,const void *e2))//函数指针用来比较两个元素的大小
//用户自定义比较函数如果第一个元素大于第二个元素返回值大于零,等于返回值等于零,小于返回值小于零

首先我们要进行模拟实现就要了解qsort函数的调用参数

void qsort(void* base,size_t num, size_t size,int (*cmp)(const void *e1,const void *e2))

该函数主要有四个参数分别为:
1.void* base     (base中存放的是待排序数据中第一个对象的地址)

2.size_t num     (排序元素中的个数)

3.size_t size      (排序元素中一个元素所占字节大小)

4.int (*cmp)(const void *e1,const void *e2)    (函数指针用来比较两个元素的大小,该函数需要用户跟据自己的需求来进行编写,以便适用于所有的排序)

通过我们了解了该函数的调用参数我们就可以完成基础的框架.

//模拟qsort实现一个冒泡排序的通用算法
void bubble_sort(void* base,
	int sz,
	int width,
	int (*cmp)(const void*e1,const 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*)base+j*width, (char*)base + (j+1) * width)>0) {
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

通过冒泡排序的思想我们大致框架基本建立完成,然而为了可以适应所有的排序,所以在数据进行交换时需要根据排序数据的首地址与元素的个数以及元素的大小推断进行交换.所以我们单独建立一个函数Swap函数.

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

Swap函数有三个参数,分别是排序元素一地址,排序元素二地址,每个元素的大小.

由于要适应所有数据类型的交换,所以我们可以将每个地址进行交换就可以.达到将元素进行交换.

注意这段代码.

if (cmp((char*)base+j*width, (char*)base + (j+1) * width)>0) {
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}

我们通过函数回调来进行两个元素进行比较,为了满足所有数据类型进行比较我们将待排序的数据的首元素地址强制类型转换成char*,由于char*类型的地址加一只会跳过一个字节,所以我们就可以通过元素的大小找到每一个元素对应的地址,来满足所有数据类型的比较.

例.如果数据类型为int,那么宽度为4,当j=0时,(char*)base就是待排序数据的首个元素的地址,(char*)base+4就是待排序数据第二个元素的地址.这样就比较好理解了.

所以Swap交换函数的参数也是这样的.

这样我们就完整模拟了qsort函数,该函数为bubble_sort函数

下面我们就测试以下好不好用

我们首先对qsort函数进行测试,通过自定义编写的比较函数,test1()为对数据类型为int的数据进行排序,test2()为对数据类型为结构体类型进行排序.

排序数据类型为int的函数为

//用户自定义比较函数如果第一个元素大于第二个元素返回值大于零,等于返回值等于零,小于返回值小于零
int cmp_int(const void* e1, const void* e2) {
	return *((int*)e1) - *((int*)e2);
}
void test1() {
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);//库里排序函数
	print(arr, sz);
}

对结构体进行比较

//结构体
struct stu{
	char name[20];
	int age;

};
//结构体比较
//结构体比较函数
//通过年龄比较
int cmp_struct_age(const void* e1, const void* e2) {
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;

}
//通过姓名进行比较
int cmp_struct_name(const void* e1, const void* e2) {
	return strcmp(((struct stu*)e1)->name , ((struct stu*)e2)->name);
}
void test3() {
	void print_stu(struct stu s[3], int sz);
	struct stu s[3] = { {"zhangsan",30} ,{"lisi",34}, {"wangwu",20} };
	int sz = sizeof(s) / sizeof(s[0]);
	print_stu(s, sz);
	qsort(s, sz, sizeof(s[0]), cmp_struct_name);
	print_stu(s, sz);
	qsort(s, sz, sizeof(s[0]), cmp_struct_age);
	print_stu(s, sz);

就是我们模拟函数bubble_sort函数的测试,由于自定义比较函数我们已经写完在这里就不在展示.

排序数据类型为int的函数为

void test2() {
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);//库里排序函数
	print(arr, sz);
}

对结构体进行比较

void test4() {
	void print_stu(struct stu s[3], int sz);
	struct stu s[3] = { {"zhangsan",30} ,{"lisi",34}, {"wangwu",20} };
	int sz = sizeof(s) / sizeof(s[0]);
	print_stu(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_struct_name);
	print_stu(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_struct_age);
	print_stu(s, sz);

}

为了方便大家察看,我写了打印函数,一个是打印数组print()函数,打印结构体函数print_stu(),总代码有备注可以自己看.那么验证以下结果吧!

 


总结

在这里我们通过了解qsort函数,应用回调函数进行模拟了一个排序方式为冒泡排序的bubble_sort函数,主要是了解函数指针,和函数回调的知识.喜欢的小伙伴可以点个小赞,这里不胜感激!谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笔写落去

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值