【C语言】qsort函数的使用和模拟实现

本篇文章我们来了解一下回C语言中qsort函数的使用方法和模拟实现。这是一个通用性很强而且非常方便的库函数,通过这篇文章希望能让你了解sort函数。

目录

一、qsort的介绍:

二、qsort函数的使用

1.qsort排序整形

2.qsort排序字符串 

3.qsort排序结构体中的整形

4.qsort排序结构体中的字符串

三、qsort的实现


一、qsort的介绍:

【C语言】qsort函数的使用和模拟实现_第1张图片

【C语言】qsort函数的使用和模拟实现_第2张图片

二、qsort函数的使用

【C语言】qsort函数的使用和模拟实现_第3张图片

qsort的使用需要引用头文件

然后我们来看qsort需要的参数,一共四个参数,具体要求如下:

void* base——待排序目标的地址

size_t  num——表示的是待排序目标需要排序的个数,可以是整个要排序的目标,也可以是目标中的一段部分

size_t——width,表示待排序数据的宽度

④表示一个函数指针,需要我们传入一个自定义的比较函数,用来比较两个数据中的大小关系。

1.qsort排序整形

【C语言】qsort函数的使用和模拟实现_第4张图片

代码如下:

//比较函数:
int compare(const void* r1, const void* r2)
{
	return (*(int*)r1 - *(int*)r2);
}

int main ()
{
    int  arr[10] = { 1,3,5,7,6,2,0,8,9,4 };
	qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(int), compare);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
    return 0;
}

这里有几个需要注意的点:

①compare函数,这个函数可以传入qsort函数中,让qsort函数知道两个数据间的大小关系,这个函数是需要我们自己创建的,我们只要书写了比较函数,我们可以比较绝大多数数据类型。

②compare函数中的参数我们必须写成const void*类型,因为void*型可以接受任意类型的数据,我们只需要传入width就可以控制操作的字节数,不会像int*、char*型将类型定死,这样就会导致qsort失去了通用性

③qsort中一个参数是一种函数指针类型,所以我们将函数的函数名传入就行了,因为函数名就表示函数地址。

④两个元素间的大小关系,如果是升序,即r1-r2,如果是降序,就是r2-r1。qsort默认的排序方式是升序,即r1-r2>0的情况

qsort函数的通用性关键就在于我们自己定义的比较函数,接下来我们写写看比较字符串、结构体里的整形/字符串如何使用qsort实现,以及对应的compare如何书写。

2.qsort排序字符串 

【C语言】qsort函数的使用和模拟实现_第5张图片

qsort函数最关键的其实就是我们书写的比较函数。我们来分析一下比较字符串函数的书写。

首先,这里我将arr1、arr2、arr3放入arr_name数组中,arr1、arr2、arr3表示首元素的地址,而arr_name也表示首元素的地址,所以arr_name是一个二级指针,我们将一些常规参数书写后开始书写comp_name函数,固定的函数参数const void*型。

其次,我们比较字符串的大小不能直接使用加减乘除,我们要使用strcmp函数来比较字符串。

然后,strcmp函数在比较完之后会返回一个值来表示两个字符串的大小关系,共有三种情况,str1>str2,会返回一个大于0的值;str1

最后,我们开始将r1指针进行强制类型转化,因为arr_name是一个二级指针,所以我们要先将r1、r2转化为char**型的二级指针,而strcmp函数比较的是一级指针,所以我们再对此指针解引用一次,就可以将对应arr1、arr2中字符串的首地址传入strcmp函数被正常调用。

代码如下:

//使用strcmp函数比较字符串
int comp_name(const void* r1, const void* r2)
{
	return strcmp( *((char**)r1), *((char**)r2));
}
int main()
{
    char arr1[20] = "zhangsan";
	char arr2[20] = "lisi";
	char arr3[20] = "wangwu";
	char* arr_name[3] = { arr1,arr2,arr3 };
	char** p = arr_name;
	qsort(arr_name, sizeof(arr_name) / sizeof(arr_name[0]), sizeof(arr_name[0]), comp_name);
	printf("%s\n", arr_name[0]);  //李四第一
	printf("%s\n", arr_name[1]);  //王五第二
	printf("%s\n",arr_name[2]);   //张三第三
}

3.qsort排序结构体中的整形

【C语言】qsort函数的使用和模拟实现_第6张图片

注意事项:

1.因为我传入的是结构体数组,所以我要将r1、r2强制类型转换为结构体指针,并且要保证结构体类型相同,都是struct stu类型的。

2.因为r1、r2是结构体指针了,我就可以直接使用r1来指向结构体中的数据,从而来比较结构体数据中的大小。

//比较函数:
int com_struct_score(const void* r1, const void* r2)
{
	return ((struct stu*)r1)->score - ((struct stu*)r1)->score;
}

int main ()
{
    //定义一个结构体数组
	struct stu student_arr[3] = { { 210603,"wangwu",90 },{ 210602,"lisi",70 } ,{ 210601,"zhangsan",80 } };
	//排序前
	printf("排序前:\n%s %d\n", student_arr->name, student_arr->score);
	printf("%s %d\n", (student_arr + 1)->name, (student_arr + 1)->score);
	printf("%s %d\n", (student_arr + 2)->name, (student_arr + 2)->score);
	//排序
	qsort(student_arr, sizeof(student_arr) / sizeof(student_arr[0]), sizeof(student_arr[0]), com_struct_score);
    //排序完成
	printf("\n\n排序后:\n%s %d\n", student_arr->name, student_arr->score);
	printf("%s %d\n", (student_arr + 1)->name, (student_arr + 1)->score);
	printf("%s %d\n", (student_arr + 2)->name, (student_arr + 2)->score);
	printf("\n");
	printf("\n");
    return 0;
}

4.qsort排序结构体中的字符串

【C语言】qsort函数的使用和模拟实现_第7张图片

注意事项:

1.我传入的是结构体指针数组,因为是数组,而且每个元素是结构体指针类型,所以我要将r1转化为结构体二级指针。

2.因为比较的是name,所以我们要再次使用strcmp函数来比较字符串大小。

3.特别注意:我们要知道()的优先级大于成员选择操作符(->),而成员选择操作符(->)的优先级又大于*号,所以我们要使用括号来保证优先级顺序:①r1先于(struct**)结合,②(struct**)r1与*号结合,表示从student_arrx数组中拿出了student1,所以r1现在指向student1,所以我们再使用成员选择操作符(->)就可以访问到这个结构体中的字符串了!但是,上面说了,成员选择操作符(->)的优先级大于*号,所以我们要用括号括起来(*(struct**)r1),再使用->。

 代码如下:

//比较函数:
int com_structx1_name(const void* r1, const void* r2)
{
	return  strcmp((*((struct stu**)r1))->name, (*((struct stu**)r2))->name);
}

int main()
{

    struct stu student1 = { 210603,"wangwu",80 };
    struct stu student2 = { 210602,"lisi",99 };
    struct stu student3 = { 210601,"zhangsan",100 };
	struct stu* student_arrx[3] = { &student1,&student2,&student3 };
	//排序前
	printf("排序前:\n%s %d\n", student_arrx[0]->name, student_arrx[0]->score);
	printf("%s %d\n", student_arrx[1]->name, student_arrx[1]->score);
	printf("%s %d\n", student_arrx[2]->name, student_arrx[2]->score);
	//开始排序
	qsort(student_arrx, sizeof(student_arrx) / sizeof(student_arrx[0]), sizeof(student_arrx[0]), com_structx1_name);
    //排序后
	printf("\n\n排序后:\n%s %d\n", student_arrx[0]->name, student_arrx[0]->score);
	printf("%s %d\n", student_arrx[1]->name, student_arrx[1]->score);
	printf("%s %d\n", student_arrx[2]->name, student_arrx[2]->score);
	//李四第一
	//王五第二
	//张三第三
    return 0;
}

三、qsort的实现

接下来我们来自己模拟实现一个qsort函数,因为我们这个实现qsort函数主要是为了实现qsort函数的通用性,所以我们可以用冒泡排序来实现qsort函数的通用性,(大家也可以使用快排算法再来模拟实现qsort函数)。

实现结果:

【C语言】qsort函数的使用和模拟实现_第8张图片

代码如下(排序的是上一个例子):

#include 
#include 
//定义结构体
typedef struct student
{
	char name[20];
	int score;
}student;
//自定义比较函数  使用strcmp函数比较字符串大小
int  compare_student(const void* r1, const void* r2)
{
	return strcmp((*((student**)r1))->name, (*((student**)r2))->name);
}
//交换函数,使用char*指针一个字节一个字节交换
void swap(char*r1,char* r2, size_t width)
{
	size_t  i = 0;
	for (i = 0; i < width; i++)
	{
		char temp = 0;
		temp = *r1;
		*r1 = *r2;
		*r2 = temp;
		r1++;
		r2++;
	}
}
//自定义qsort函数的实现
void My_bubble_qsort(void* base, size_t num, size_t width, int (*compare)(const void*r1 ,const void*r2 ))
{
	size_t i = 0;
	size_t j = 0;
	for (i = 0; i < num-1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
int main()
{
	student wangwu = { "wangwu",75 };
	student lisi = { "lisi",94 };
	student zhangsan = { "zhangsan",60 };
	student* arr_student[3] = { &wangwu,&lisi,&zhangsan };//定义结构体指针数组
	int i = 0;
	printf("排序前:\n");
	for (i = 0; i < 3; i++)
	{
		printf("%s,%d\n", arr_student[i]->name, arr_student[i]->score);
	}
	My_bubble_qsort(arr_student, sizeof(arr_student) / sizeof(arr_student[0]),sizeof(arr_student[0]),compare_student );
	printf("\n排序后:\n");
	for (i = 0; i < 3; i++)
	{
		printf("%s,%d\n", arr_student[i]->name, arr_student[i]->score);
	}
	return 0;
}

My_bubble_qsort实现讲解:

1.设置参数:我们将base设置为void*型,num、width设置为size_t(无符号整形),一个返回值为int型,两个参数为void*型的函数指针。

2.在判断条件中,判断是否进行两个数据间的交换,我们设置在只有compare函数返回大于0才进行。

3.传到compare函数中参数设置,这里我们将要比较的类型先转化为char*型,指向一个字节,因为char*型指针指向一个字节,我们不知道传入的类型大小是多少,所以我们可以先设置为char*型指针指向传入的元素我们可以再加上宽度 ,这就可以让char*指针指向下一个数据,如果我们再将(width*j),这就可以传入这一串数据中的第j个元素。这样第一个参数就设置好了,因为冒泡排序是两个相邻的元素比较,所以第二个元素我们只用再j的基础上+1就可以表示下一个元素了。

4.交换函数(swap),当我们发现compare函数的返回值大于1时,我们就要开始进行交换。先将要交换的两个元素以char*型的形式传入,再将width传入swap函数。这时使用char*型指针的优势就体现了出来,因为char*型指针只改变一个字节,所以我们再将width传入,使用char*一个字节一个字节进行交换,直到i

qsort是一个通用性很强的函数,可以排序绝大多数的数据结构,但是关于qsort的使用,仍需要我们大量练习才可以熟练掌握。

好了,本篇文章就到这结束了,感觉不错的朋友门可以留个赞噢~

你可能感兴趣的:(C语言基础,c语言,后端,c++)