Lesson 16 指针6

文章讲述了如何使用冒泡排序算法对任意类型数据进行排序,涉及void*指针、元素个数、元素宽度以及cmp和Swap函数的设计。同时,文章对比了sizeof和strlen的作用,并给出了相关面试题的示例分析。

今天这节课分为两个部分,第一个是利用冒泡排序的思想,仿照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. 不是地址的话就是看类型,记住各个类型都占几个字节即可;
基于分布式模型预测控制的多个固定翼无人机一致性控制(Matlab代码实现)内容概要:本文围绕“基于分布式模型预测控制的多个固定翼无人机一致性控制”展开,采用Matlab代码实现相关算法,属于顶级EI期刊的复现研究成果。文中重点研究了分布式模型预测控制(DMPC)在多无人机系统中的一致性控制问题,通过构建固定翼无人机的动力学模型,结合分布式协同控制策略,实现多无人机在复杂环境下的轨迹一致性和稳定协同飞行。研究涵盖了控制算法设计、系统建模、优化求解及仿真验证全过程,并提供了完整的Matlab代码支持,便于读者复现实验结果。; 适合人群:具备自动控制、无人机系统或优化算法基础,从事科研或工程应用的研究生、科研人员及自动化、航空航天领域的研发工程师;熟悉Matlab编程和基本控制理论者更佳; 使用场景及目标:①用于多无人机协同控制系统的算法研究与仿真验证;②支撑科研论文复现、毕业设计或项目开发;③掌握分布式模型预测控制在实际系统中的应用方法,提升对多智能体协同控制的理解与实践能力; 阅读建议:建议结合提供的Matlab代码逐模块分析,重点关注DMPC算法的构建流程、约束处理方式及一致性协议的设计逻辑,同时可拓展学习文中提及的路径规划、编队控制等相关技术,以深化对无人机集群控制的整体认知。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值