qsort函数以及模拟

文章目录

概要

本次我们将要学习一个库函数,该函数可以将你需要排序的数据进行排序,任何类型的数据都可以,比如整形数组,字符数组,或者结构体。并且本章我也会自己写一个函数模拟qsort的实现。

qsort函数介绍

qsort是一个C语言里面的库函数,它用于将用户指定给它的数据进行排序,它的底层逻辑是使用快速排序算法。

函数引用的头文件

函数需要包含头文件stdio.h

函数参数介绍

void qsort(void* base, size_t num,
	size_t size,
	int (*cmp)(const void* p1, const void* p2));

这是函数的声明,其中它的参数有四个。

第一个是无类型指针,它用于接收第一个数据的地址。

第二个是正整数类型的整数,它用于接收数据元素的总个数

第三个是正整数类型的整数,它用于接收第一个元素的大小,相当于所占内存空间。

第四个是一个函数指针,它指向的函数返回类型是int,参数是两个const修饰的无类型指针。

下面先说说那个函数指针指向的函数需要满足的条件。

函数指针

函数指针指向的那个函数需要用户自己写出来,作用是比较两个数据的大小,如果前一个数比后一个数大,那么就返回大于0的数,如果相等则返回0,如果小于则返回小于0的数。

假如你想比较两个整数的大小,你可以这样写。

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1-*(int *)p2;
}

提醒一下,由于p1,p2是无类型指针,不能直接解引用,所以需要

先强制转换成int*类型,才能解引用,比如上面这段代码,前一个数比后一个数大,则直接作差返回差值,便满足了函数需求。

介绍完了qsort函数,下面讲讲如何使用

//测试qsort排序整形数组(升序)
void print_int_arr(int arr[], int sz)
{
	int i;
	for (i = 0; i < sz; i++)
		printf("%d ",arr[i]);
	printf("\n");
}
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1-*(int *)p2;
}
void test1()
{
	int arr[10] = { 2,3,5,1,4,7,6,9,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_int_arr(arr, sz);
}
int main()
{
	test1();

	return 0;
}

在test1()中,我们实现了整形数组进行升序排序,讲讲传参的过程,arr代表数组首元素的地址,也就是qsort需要的第一个数据的地址,第二个是数组元素个数,也就是qsort需要的数据个数,第三个是用sizeof操作符计算出来首元素的大小,单位是字节,第四个传的是我自己写的比较函数的地址,函数名相当于地址。

最后打印一下数组,看看排序效果。

再试试结构体的排序

//测试排序结构体的年龄大小
struct STU
{
	char name[10];
	int age;
};
int cmp_stu_age(const void* p1, const void* p2)
{
	return ((struct STU*)p1)->age - ((struct STU*)p2)->age;
}
void test3()
{
	struct STU arr[3] = { {"zhangsan",59},{"lisi",19},{"madan",70} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_age);
}

成功将结构体每个成员的年龄进行排序。

学会了如何使用qsort,那么下面就模拟实现一下qsort函数吧。

qsort函数模拟实现:

qsort函数排序的算法使用的是快速算法,而我使用的是冒泡排序的算法

我的参数设置和qsort的设置很相似,就不再过多赘述,讲讲函数内部。

void bubble_myqsort(void* base, size_t sz, size_t width, int (*p)(const void* p1, const void* p2))
{
	int i, j;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (p((char*)base + width * j, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + width * j, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

在相邻元素比较的时候,我们需要判断两个数是否需要交换位置,先用函数指针调用,比较两个数据的大小,由于函数调用时,不确定指针指向的是哪种类型的数据,在下一次比较两个数据的时候,不能知道指针跳过多少字节,所以先将指针类型强制转换成char*类型,然后再用width获得每个数据的内存空间大小,则只需要让指针+width*j则可以跳到下一个数据的地址

在交换时,因为不知道时哪种类型的数据,所以直接交换位置是行不通的,那么我们就需要获得两个数据的地址来进行相办法交换。

讲讲我的swap函数是如何交换两个未知类型数据的。

void swap(char* p1, char* p2, size_t width)
{
	int i;
	char temp;
	for (i = 0; i < width; i++)
	{
		temp = *(p1 + i);
		*(p1 + i) = *(p2 + i);
		*(p2 + i) = temp;
	}
}

参数为两个字符类型指针,用于存储两个数据的地址,width用于存放数据所占字节数。

不知道数据类型,则不能转换成对应类型的指针,也就不能解引用来进行交换,那么获得数据第一个字节的地址,以及它所占的宽度,那么我们就可以一个一个字节的交换,如上面代码,for循环width次,将每个字节的数据依次交换,则实现了两个数据位置的交换

下面试试能不能将整形数据排序。

void test5()
{
    int arr[10] = { 2,3,5,1,4,7,6,9,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_myqsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_int_arr(arr, sz);
}

ok,实现目标。

小结

qsort函数主要解决的是它可以让你不用思考使用哪种排序的算法,你只要将需要排序的数据传参给它,以及自己实现一个满足它要求的函数,用来比较两个数据大小的函数,则可以实现使用qsort函数排序。

再贴一下本次学习的qsort的测试,整形数组排序,字符数组排序,结构体姓名排序,结构体中的年龄排序,以及模拟实现的测试代码。需要的话可以自取。

头文件说明:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"qsort.h"
//测试qsort排序整形数组(升序)
void print_int_arr(int arr[], int sz)
{
	int i;
	for (i = 0; i < sz; i++)
		printf("%d ",arr[i]);
	printf("\n");
}
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1-*(int *)p2;
}
void test1()
{
	int arr[10] = { 2,3,5,1,4,7,6,9,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_int_arr(arr, sz);
}

//测试排序字符数组
int cmp_char(const void* p1, const void* p2)
{
	return *(char*)p1 - *(char*)p2;
}
void print_char_arr(char arr[], int len)
{
	int i;
	for (i = 0; i < len; i++)
		printf("%c", arr[i]);
	printf("\n");
}
void test2()
{
	char arr[] = "acdbfe";
	int len = strlen(arr);
	qsort(arr, len, sizeof(arr[0]), cmp_char);
	print_char_arr(arr, len);
}

//测试排序结构体的年龄大小
struct STU
{
	char name[10];
	int age;
};
int cmp_stu_age(const void* p1, const void* p2)
{
	return ((struct STU*)p1)->age - ((struct STU*)p2)->age;
}
void test3()
{
	struct STU arr[3] = { {"zhangsan",59},{"lisi",19},{"madan",70} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_age);
}

//测试排序结构体的名字
int cmp_stu_name(const void* p1, const void* p2)
{
	return strcmp(((struct STU*)p1)->name, ((struct STU*)p2)->name);
}
void test4()
{
	struct STU arr[3] = { {"zhangsan",59},{"lisi",19},{"madan",70} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_name);
 }

//实现自己的bubble_qsort函数
void swap(char* p1, char* p2, size_t width)
{
	int i;
	char temp;
	for (i = 0; i < width; i++)
	{
		temp = *(p1 + i);
		*(p1 + i) = *(p2 + i);
		*(p2 + i) = temp;
	}
}
void bubble_myqsort(void* base, size_t sz, size_t width, int (*p)(const void* p1, const void* p2))
{
	int i, j;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (p((char*)base + width * j, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + width * j, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

//测试使用自己的qsort排序整形数据
void test5()
{
    int arr[10] = { 2,3,5,1,4,7,6,9,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_myqsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_int_arr(arr, sz);
}

//测试使用自己的qsort排序字符数据
void test6()
{
	char arr[] = "acdbfe";
	int len = strlen(arr);
	bubble_myqsort(arr, len, sizeof(arr[0]), cmp_char);
	print_char_arr(arr, len);
}

//测试使用自己的qsort排序结构体名字数据
void test7()
{
	struct STU arr[3] = { {"zhangsan",59},{"lisi",19},{"madan",70} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_myqsort(arr, sz, sizeof(arr[0]), cmp_stu_name);
}


int main()
{
	//test1();
	//test2();
	//test3();
	//test4();
	
	test5();
	//test6();
	//test7();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值