第13讲:深入理解指针(4)续

qsort的模拟实现:

在qsort函数的定义中,有些参数的类型是size_t,为了尽可能还原qsort函数,我们也采用了size_t作为参数类型。这种方法涉及到大量的类型转换,仅供参考,教学部分在下一个代码

参考:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
	int age;
	char name[15];
}arr[] = { {16,"zhansan"},{13,"lisi"},{15,"wangwu"},{11,"cuihua"},{18,"lihua"} };
int cmp(const void* p1, const void* p2)
{
	return (((struct stu*)p1)->age - ((struct stu*)p2)->age);
}
void swap(const void* p1, const void* p2,size_t sz)
{
	int i;
	char t;
	for (i = 0; i < (int)sz; i++)
	{
		t = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = t;
	}
}

void myqsort(const void* p1, size_t num, size_t sz, int(*p2)(const void*,const void*))
{
	int i, j;
	for (i = 0; i < (int)num - 1; i++)
	{
		for (j = 0; j < (int)num - 1 - i; j++)
		{
			if (p2((char*)p1 + j * (int)sz, (char*)p1 + (j + 1) * (int)sz) > 0)
				swap((char*)p1 + j * (int)sz, (char*)p1 + (j + 1) * (int)sz, sz);
		}
	}
}
int main()
{
	int i;
	myqsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i].age);
		printf("%s ", arr[i].name);
		putchar('\n');
	}
	return 0;
}

其实两段代码区别并不大,但是为了教学,我们讲下面的这个方法

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
	int age;
	char name[15];
}arr[] = { {16,"zhansan"},{13,"lisi"},{15,"wangwu"},{11,"cuihua"},{18,"lihua"} };
int cmp(const void* p1, const void* p2)
{
	return (((struct stu*)p1)->age - ((struct stu*)p2)->age);
}
void swap(const void* p1, const void* p2,int sz)
{
	int i;
	char t;
	for (i = 0; i < sz; i++)
	{
		t = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = t;
	}
}

void myqsort(const void* p1, int num, int sz, int(*p2)(const void*,const void*))
{
	int i, j;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (p2((char*)p1 + j * sz, (char*)p1 + (j + 1) * sz) > 0)
				swap((char*)p1 + j * sz, (char*)p1 + (j + 1) * sz, sz);
		}
	}
}
int main()
{
	int i;
	myqsort(arr, (int)sizeof(arr) / (int)sizeof(arr[0]), (int)sizeof(arr[0]), cmp);
	for (i = 0; i < (int)sizeof(arr) / (int)sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i].age);
		printf("%s ", arr[i].name);
		putchar('\n');
	}
	return 0;
}

参数:qsort能排列任意类型的数据,所以传数据首地址时,qsort函数应该用void*类型的形参接受,后面几个参数的目的体现在函数的实现上。

返回值:我们只需排序,不需要返回值,所以设置返回值类型为void。

外面的框架与冒泡类似,我们发现,仅仅是比较的部分和交换的部分变化了(因为这个函数要完成的也是排序),冒泡就不再赘述了。

比较部分:p2是我们自定义的函数(比较方法,该函数返回的值决定是否要交换)的地址,可以直接当函数使用(不用解引用)。现在需要将要比较的参数上传上去。

现在的问题:我们有首元素的指针,但是只有const void*类型的,不能直接解引用,需要类型转换。转成什么样的类型,直接决定了解引用后一次访问几个字节。我们想要一次访问多少字节呢?一个元素占多少个字节就要多少个(因为这样的话,让地址加上几个整数,就能跳过几个元素)。所以啊,直接将其强转成元素的类型刚刚好。但是啊,类型时不能作为参数传给函数的。所以,我们要想别的办法。

这时候,聪明的程序员想到了一个办法,将首元素的大小(大小为sz)上传上去,再将p1(首元素地址)强制类型转换为char*类型,地址加一将跳过一个字节,如果想要跳过N个元素,就让首地址加上N*sz,这样就能顺藤摸瓜找到每个元素。

如果用于比较的函数返回一个整数,就交换(与前面说的相呼应)。如果要交换,就将要交换的两个元素的地址传给swap函数,这个函数的功能就是交换位置。

一样的问题,我们有了需交换的变量的地址,还时希望能直接强转成该变量的类型,但是,类型不能作为参数上传。

聪明的程序员又想了一个办法:将元素大小也上传上去(一共要转换的字节数),再将地址的类型强转成char*,一次就转一个字节。

在循环中,第一次转换两变量的第一个字节。

在循环中,第二次转换两变量的第二个字节。

在循环中,第三次转换两变量的第三个字节。

…………

在循环中,第N次转换两变量的第N个字节。

直到交换完每一个字节。

这样,函数的思想就分析完了。

最后,我们对将一个字节拆分成多个字节转换这一方法进行可行性分析。

可能会有人问,将一个数据拆开,那表达的还是那个数据吗?这种方法为什么行?

其实,数据在内存中是以二进制补码的形式组成的,转换的实质时转换二进制数。

我们这种方法就好比将两个相同位数(数据类型相同)的十进制数对位转换。

比如:10101,46464这两个数字

我们先交换万位:40101,16464

再交换千位:       46101,10464

…………

最后交换个位:  46464,10101

转换成功,数字并没有因拆分转换而改变。所以,这种方法是靠谱的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

INUYACHA

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

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

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

打赏作者

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

抵扣说明:

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

余额充值