Lesson 16 指针6

今天这节课分为两个部分,第一个是利用冒泡排序的思想,仿照qsort函数来实现bubble_sort版的可以排序任意类型数据的函数。另一个部分是对指针的应用(一些面试题)。

bubble_sort实现

先捋一下冒泡排序的基本思想:

  1. 排序趟数由元素个数决定;
  2. 每趟内只能将一个元素排到正确的位置;
    然后看一下最基本的代码(假设排序的是int类型的数据):
void bubble_sort(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j]>arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

现在想利用这种思想来排序任意类型的数据,所以函数需要重新被设计。

函数设计

还是先说思路,然后按照思路写代码。

  1. 排序时肯定需要知道数据的起始位置,这里可以用一个指针来表示出来。但由于是任意类型的数据,所以需要使用一个可以接收任意类型指针的东西作为参数。那么可以接收任意类型指针的东西就是void*,那么函数的第一个参数就可以设计为 void* base;
  2. 排序的过程中也一定需要元素个数,这里用一个 size_t sz来接收;
  3. 知道了元素个数,那也一定需要元素的大小,否则是没法进行排序的,这里就用一个size_t width来接收;
  4. 有了这些,参考qsort,就需要一个函数来比较数据中的两个元素了,同样,可以设计为:int (*cmp)(const void* e1, const void* e2)

那么,函数就可以设计成:

void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))

依据冒泡排序的思想,参考上面的代码,可以开始实现这个函数:

void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//if (arr[j]>arr[j + 1])
			if (cmp() > 0)
			{
				//交换
				//int tmp = arr[j];
				//arr[j] = arr[j + 1];
				//arr[j + 1] = tmp;
				Swap();
			}
		}
	}
}

判断部分交给cmp函数去处理,而交换部分就不能写成之前int型的了,需要独立设计一个函数来交换两个元素。
到这里,继续思考如何实现cmpSwap。可以继续参考qsort。首先思考cmp

  1. cmp的返回值如何设计?参考一下qsort里函数指针的写法,那么可以写出来:int cmp(const void* e1, const void* e2)
  2. 传进来的元素可能是任意的类型,那么怎么去利用cmp函数呢?类型不同,那么指针的运算就没有办法写死,再思考一步,就是如何让cmp实现类似 arr[j]>arr[j+1] 这种模式呢?传进来的元素有首地址,还有宽度,那就思考一下该怎么样利用这些信息。数据的首地址是base,想找到数据中的第j个元素,那么就可以这样做:(char*)base + j*width。由于char类型的数据只占1个字节,将base强制转换为char*之后,再+j*width就是第j个元素的首地址了。所以,对于任意类型的数据,给cmp传参为:cmp((char*)base+j*width,(char*)base+(j+1)*width),就可以利用外部实现的cmp来计算了。

然后看一下Swap

  1. 交换的是任意类型的数据,那么也可以一个字节一个字节的交换;
  2. 交换的多少字节还是width决定,那么Swap可以这设计:
void Swap(char* buf1, char* buf2,size_t width)

实现就比较简单了:

void Swap(char* buf1, char* buf2, size_t width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		*buf1 ^= *buf2;
		*buf2 ^= *buf1;
		*buf1 ^= *buf2;
		buf1++;
		buf2++;
	}
}

有了这些,完整的写一个冒泡排序:

void Swap(char* buf1, char* buf2, size_t width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		*buf1 ^= *buf2;
		*buf2 ^= *buf1;
		*buf1 ^= *buf2;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base+j*width,(char*)base+(j+1)*width) > 0)//需要传待比较的两个元素的地址
			{
				Swap((char*)base + j*width, (char*)base + (j + 1)*width,width);
			}
		}
	}
}

sizeof和strlen的区别

  1. sizeof是看目标所占空间的大小(字节计),不关心目标在内存里存了什么;
  2. sizeof是操作符
  3. strlen是计算字符串中‘\0’之前元素的个数;
  4. strlen是库函数。
    后面的一些面试题会和这两个功能有关。

面试题

一维数组

先看一组代码:

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

一个一个分析一下,但把握下面的原则:

  1. sizeof(数组名)中的数组名表示整个数组;
  2. &(数组名)也表示数组的地址;
  3. 其余情况数组名均作为首元素地址处理。

printf("%d\n",sizeof(a)); :这里a表示整个数组,因此结果是16;
printf("%d\n",sizeof(a+0));:这里表示的是数组中的第一个元素的地址,所以结果是4/8;
printf("%d\n",sizeof(*a));:这里对数组名解引用,而a此时是首元素地址,相当于对首元素解引用之后判断大小,数组是int,所以结果是4;
printf("%d\n",sizeof(a+1));:这里a还表示首元素地址,首元素地址+1到第二个元素的地址,所以这里表示的是第二个元素地址的大小,还是4/8;
printf("%d\n",sizeof(a[1]));:这里就是第二个元素的大小,int型,所以是4;
printf("%d\n",sizeof(&a)); :取地址表示整个数组的地址,但本质还是地址,那么4/8;
printf("%d\n",sizeof(*&a));:这里取地址和解引用抵消,16;
printf("%d\n",sizeof(&a+1));:这里是跳过了整个数组,指向了4之后的地址,但还是地址,4/8;
printf("%d\n",sizeof(&a[0]));:第一个元素的地址,4/8
printf("%d\n",sizeof(&a[0]+1));:第一个元素的地址+1,第二个元素的地址,还是地址,4/8。

再来一组:

	char arr[] = { 'a', 'b', 'c', 'd', 'e','f' };
	printf("%d\n", sizeof(arr));//6
	printf("%d\n", sizeof(arr + 0));//4/8 地址
	printf("%d\n", sizeof(*arr));//1 首元素a
	printf("%d\n", sizeof(arr[1]));//1 第二个元素b
	printf("%d\n", sizeof(&arr));//4/8 地址
	printf("%d\n", sizeof(&arr + 1));//4/8 地址,f之后的第一个地址
	printf("%d\n", sizeof(&arr[0] + 1));//4/8 地址,b的地址

简单做个总结:

  1. sizeof主要是看操作数的类型,如果能确定是地址,那么结果就是4/8;
  2. 不是地址的话就是看类型,记住各个类型都占几个字节即可;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值