c学习笔记——自定义qsort函数

目录

一、前言

 二、从MSDN认识qsort

1.分析:

2.void*的特殊性

三、cmp_函数的实现

1.比较int类型数据

2.比较char类型数据

3.比较float类型函数

4.比较结构体类型函数

四、bubble_sort函数的实现

分析:

五、swap函数的实现

六、重难点总结


一、前言

   我们学习过的冒泡排序,插入排序,选择排序等经典排序方法,为我们数据排序提供了稳定思路,但局限在于排序数据类型的单一。为了解决这一问题,c语言中提供了库函数qsort解决。我们今天就通过自定义函数实现qsort的功能,排序方法采用基本的冒泡排序


 二、从MSDN认识qsort

1.分析:

我们需要传入四个变量:base——待排序数组的首元素地址;

                                        num——数组内的元素个数;

                                        width——每个元素的大小(单位为字节);

                                       函数指针——传入两个数的地址,比较二者大小(需自己设计)

2.void*的特殊性

1.可以传入任何类型的指针而不会报警告,适合我们实现任何数据类型排序的目的;

2.不可以对其进行解引用操作,除非通过强制类型转换()确定其具体类型;

3.不可以对其进行加减运算,原因是不知道类型所以不知道步长,因此还是需要强制类型转换


三、cmp_函数的实现

注意:都需要先进行强制类型转换才可以解引用

1.比较int类型数据

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

2.比较char类型数据

char类型数据不可以用<>=进行比较,所以要用函数strcmp库函数,头文件为#include<string>

int cmp_char(const void*e1, const void*e2)
{
	return strcmp((char*)e1, (char*)e2);
}

3.比较float类型函数

因为返回的是int类型,所以我们加入了判断语句。用数的正负表示数的大小情况

int cmp_float(const void*e1, const void*e2)
{
	if ((float*)e1 > (float*)e2)
		return 1;
	else if ((float*)e1 ==(float*)e2)
		return 0;
	else
		return -1;
}

4.比较结构体类型函数

结构体数组元素不可以直接比较,必须先选出结构体某一内容进行比较

typedef struct book{ 

	char name[20];
	int prince;
                   }b;//重命名struct book为b


int cmp_stru_price(const void*e1, const void*e2)
{
	return ((b*)e1)->prince - ((b*)e2)->prince;
}

注意!!!(b*)e1外面的括号不可以省略,因为->的优先级比强制类型转换高


四、bubble_sort函数的实现

首先思考一个问题:既然传入的指针为void*类型,如何实现步长的确定?聪明的科学家想到利用width作为我们的标准

void bubble_sort(void*base,int sz,int width,int(*cmp)(void*,void*))
{
	int i; int j;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1-i; j++)
		{
			if (cmp((char*)base + width*j, (char*)base + width*(j + 1))>0)
			{
				swap((char*)base + width*j, (char*)base + width*(j + 1),width);
			}
		}

	}
}

分析:

1.int(*)(void*,void*)是一个函数指针,指向我们之前设计的大小比较函数

2.在使用base的时候要先强制类型转换才可以作加减运算

3.漂亮的地方在于,不管实际传入的base是什么类型,我们都将其转化为char*类型的指针,因为char*的步长最小为1,加上width就可以准确指向下一个 

以int为例,width为4,所及就可以指向下一个元素


五、swap函数的实现

void swap(char*buff1,char*buff2,int width)
{
	int i;
	for (i = 0; i < width; i++)
	{
		int tmp = *buff1;
		*buff1 = *buff2;
		*buff2 = tmp;
		buff1++;
		buff2++;

	}

}

值得关注的是,交换不是一次就可以了,因为我们现在是char*类型的指针,如上图所画,一次只能交换一格,如交换int的,要交换4次


六、重难点总结

1.函数指针的使用,使我们每次只需导入大小比较函数的地址即可,而不必写多个函数,分别含有不同的大小比较函数,减少了重复代码的出现

 2.width的使用相当于告诉了我们进行比较的数组元素的类型,满足我们实现各种数据类型排序的需求

3.void*函数海纳百川,方便了我们的输入,只是注意在使用时的强制类型转化,否则无法使用


希望上述的总结对大家的理解有帮助,也更好的欣赏回调函数(这里为用函数指针调用函数)的魅力。

猜你喜欢

转载自blog.csdn.net/whc18858/article/details/120028075