模拟实现qsort函数

目录

一、简单回顾qsort函数

二、通过冒泡排序的算法模拟实现qsort函数

1>参数改造

2>bubble_sort函数体改造

三、完整代码演示


上篇博客对qsort函数的概念和用法进行了讲解,今天来深入了解qsort函数,模拟实现qsort函数,在此之前对qsort函数进行简单回顾:

一、简单回顾qsort函数

概念:qsort是一个库函数,其所需的头文件为 #include它可以用来对任意类型的数据进行排序

用法

qsort函数有4个参数:

void* base:待排数据的首元素的地址

size_t num:待排数据的个数

size_t size:待排数据中一个数据所占字节大小

int (*compar) (const void*, const void*):是一个函数指针,函数指针compar指向一个函数,这个函数需要自己写,这个函数功能就是比较待排序数组的两个元素

关于qsort函数的详细讲解可以看一下我之前写过的博客:指针进阶讲解(2)


二、通过冒泡排序的算法模拟实现qsort函数

冒泡排序是我们熟悉的一个排序算法,所以用它来模拟实现qsort函数,先来看一个冒泡排序来排序整型数据的代码:

#include

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	int j = 0;
	for (i = sz - 1; i > 0; i--) //趟数
	{
		for (j = 0; j < i; j++) //一趟里两数交换次数
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	return 0;
}

接下来通过对该代码的改造来达成我们的目的

1>参数改造

在bubble_sort函数调用处,函数的第一个实参是arr也就是数组首元素的地址,而我们要排序的是任意类型的数据,那么在函数定义处就不能用int arr[]来接收它,所以应该改为void*类型的指针,因为void*类型的指针可以接收任意类型数据的地址,为了和qsort函数一致,我们改为void* base;

在bubble_sort函数调用处,函数的第二个实参是sz,也就是数组长度,这里没有要改的地方,为了和qsort函数保持一致,我们将类型改为size_t;

在冒泡排序算法中是从前往后两两数据依次比较,每比完两个数据之后往后走,比较接下来的两个数据,但是,我们要比较的是任意类型的数据,那么比完两个数据之后往后走多上呢?计算机时不得而知的,所以要加上第三个参数:size_t size也就是待排数据中一个数据所占字节的大小;

在bubble_sort函数内,是这样比较两个数据的:

if (arr[j] > arr[j + 1])
        {
              ...
        }

这种方法显然无法满足任意类型数据的比较,比如结构体类型的数据,此时就需要一个新的比较方法,我们将这种方法写进另一个函数cmp中,参照qsort函数的最后一个参数,我们需要第四个参数函数指针,再利用函数指针去回调新的比较方法的函数,此时这个bubble_sort函数的参数如下:

void bubble_sort(void*base, size_t num, size_t size, int (*cmper)(const void*, const void*))

2>bubble_sort函数体改造

int cmp(const void*a, const void*b)
{
    return *(int*)a - *(int*)b;
}

void bubble_sort(void*base, int sz, size_t size, int (*cmper)(const void*, const void*))
{                                                //该函数指针指向cmp函数
	int i = 0;
	int j = 0;
	for (i = sz - 1; i > 0; i--)
	{
		for (j = 0; j < i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	
    bubble_sort(arr, sz, sizeof(arr), cmp);
	return 0;
}

在bubble_sort函数中两个for循环都不用改变,因为它们都取决于待排数据的个数,唯一要进行改造就是两个数据的比较方法和两个数据的交换方法

if (arr[j] > arr[j + 1]) //改造
        {
             /*int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;*/ 改造
        }

首先是比较方法,也就if的条件语句应该去调用我们刚刚写好的新的比较方法的函数cmp,函数指针int (*cmper)(const void*, const void*)所指向的函数的参数是两个指针,所以在调用cmp函数时应该传过去的是arr[j]和arr[j+1]的地址;

那么该如何求arr[j]和arr[j+1]地址呢,base是数组首元素的地址,但是不能用base + j来表示arr[j]的地址,因为base是void*类型的,void*类型的指针不能解引用和+-操作,如果我们排序的是整型数据,可以将其强转为int*类型然后在进行+-操作,但是这样就只能排序整形数据,其他类型数据行不通;

这里介绍一种方法:(char*)base + j * size;将其强转为char*类型,因为char*类型的指针+1-1时只移动一个字节,char*类型的base + size,意思是让它移动待排数据类型所占的字节个数,让size*j就代表第j个元素的地址

模拟实现qsort函数_第1张图片

if (cmp((char*)base + size * j, (char*)base + size*(j + 1))

{

        ... 

}

接下来对两个数据的交换方法进行改造,在不知道是什么数据类型情况下,我们可以将两个数据进行一个字节一个字节的交换

void swap(char* buf1, char* buf2, size_t size)
{
    int i = 0;
    for (i = 0; i < size; i++)
    {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

if (cmp((char*)base + size * j, (char*)base + size*(j + 1))
{
     swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}

将要交换的两个数据的地址,和每个数据所占内存大小传给swap函数,swap函数用两个字符指针来接收两个数据的地址,在函数内部将buf1和buf2解引用再交换,由于它们是两个字符指针,所以每次交换只交换一个字节,交换完后buf1和buf2分别+1,进行下一个字节的交换

至此就完成了用冒泡排序的算法模拟实现psort函数

三、完整代码演示

排序整形数据:

#include

void print(int arr[], size_t sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void swap(char* buf1, char* buf2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, size_t num, size_t size, int (*cmper)(const void*, const void*))
{
	int i = 0;
	int j = 0;

	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmper((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

int cmp_int(const void* a, const void* b)
{
	return *(int*)a - *(int*)b;
}

int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	size_t sz = sizeof(arr) / sizeof(arr[0]);

	print(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	print(arr, sz);

	return 0;
}

结果:

排序结构体数据:

#include

struct Stu
{
	char name[20];
	int age;
};

void swap(char* buf1, char* buf2, size_t size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, size_t num, size_t size, int (*cmper)(const void*, const void*))
{
	int i = 0;
	int j = 0;

	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmper((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

int cmp_struct_name(const void* a, const void* b)
{
	return strcmp(((struct Stu*)a)->name, ((struct Stu*)b)->name);
}

int main()
{
	struct Stu arr[3] = { {"sans", 18}, {"frisk", 8},{"chara", 9} };
	size_t sz = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(arr, sz, sizeof(arr[0]), cmp_struct_name);

	return 0;
}

结果:

模拟实现qsort函数_第2张图片

你可能感兴趣的:(C语言知识点,c语言)