指针进阶(3)

2023-09-20 22:13:40

9. 模拟实现排序函数

这里我们使用冒泡排序算法,模拟实现一个排序函数,可以排序任意类型的数据。

这段代码可以排序整型数据,我们需要在这段代码的基础上进行改进,使得它可以排序任意类型的数据。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
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 temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test1()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//升序
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);//排序前打印
	bubble_sort(arr,sz);//排序成降序
	print(arr, sz);//排序后打印

}
int main()
{
	test1();//排序整型数据
}

那么我们要做的就是将bubble_arr里的int arr[]改为指针的形式,并且是void*类型,这样就可以接收任意类型的数据,再将int sz改为size_t类型,仅仅有元素个数还不够,我们还需要知道一个元素的大小,这样可以方便我们访问,所以我们加一个size_t size,最后我们再加一个比较函数int (*cmp)(const void* e1,const void* e2),这是一个函数指针,所以我们需要根据自己的需求写一个比较函数。

e1是一个指针,存放了一个要比较的元素的地址
e2是一个指针,存放了一个要比较的元素的地址
e1指向的元素>e2指向的元素,返回>0的数字
e1指向的元素==e2指向的元素,返回0
e1指向的元素<e2指向的元素,返回<0的数字

1.如果我们要比较整型数据,我们就写一个cmp_int,将const void*强转成(int*)再解引用,然后两个元素进行相减,结果作为这个函数的返回值。由于base的类型是void*,所以不能进行+1,-1,所以我们再将base的类型强转成char*,然后加上j*size,就可以进行每个元素的访问了。我们在进行排序的时候需要将元素交换,所以写一个交换函数swap,将元素的地址和大小传过去就行了。

2.如果我们要比较结构体数据的首字母,我们写一个cmp_stu_by_name作为比较函数,字符串的比较我们用strcmp,返回值是>0,<0或者0。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
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 (*cmp)(const void* e1, const void*e2))
{
	//冒泡排序的趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		//一趟冒泡排序
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			//if (arr[j] > arr[j + 1])
			if(cmp((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*e1, const void*e2)
{
	return *(int*)e1 - *(int*)e2;
}
void test1()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };//升序
	//排序为降序
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}	
struct Stu
{
	char name[20];//20
	int age;//4
};
int cmp_stu_by_age(const void* e1, const void*e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test2()
{
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15}};
	int sz = sizeof(arr) / sizeof(arr[0]);//3
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{
	//整型数据/字符数据/结构体数据...
	//可以使用qsort函数对数据进行排序
	//测试bubble_sort,排序整型数据
	test1();
	//测试bubble_sort,排序结构体的数据
	//test2();
	return 0;
}

10. 指针和数组笔试题解析

一维数组:

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

数组名的理解:
数组名是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
2. &数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址

所以答案是4*4=16。

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

a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+0还是首元素的地址。

只要是地址,大小就是4/8,单位是byte。

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

a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址.*a == *(a+0) == a[0]。

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

a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+1就是第二个元素的地址。a+1 == &a[1] 是第2个元素的地址,是地址就是4/8个字节。

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

a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小,单位是字节 - 4

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

&a - 是取出数组的地址,但是数组的地址也是地址,是地址就是4/8个Byte。

数组的地址 和 数组首元素的地址 的本质区别是类型的区别,并非大小的区别
    a  -- int*             int * p = a;
   &a -- int (*)[4]     int (*p)[4] = &a;

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

对数组指针解引用访问一个数组的大小,单位是字节。

sizeof(*&a) --- sizeof(a) //16

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

&a数组的地址,&a+1还是地址,是地址就是4/8个字节。

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

&a[0]是首元素的地址, 计算的是地址的大小 4/8 个字节。

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

/&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址,大小4/8个字节。

字符数组

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));

随机值,arr是首元素的地址.

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr + 0));

随机值,arr是首元素的地址, arr+0还是首元素的地址。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(*arr));

err(发生错误),arr是首元素的地址, *arr就是首元素 - 'a' - 97。
站在strlen的角度,认为传参进去的'a'-97就是地址,97作为地址,直接进行访问,就是非法访问。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr[1]));

err, 'b' - 98。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(&arr));

随机值
&arr -- char (*)[6]
const char*。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(&arr + 1));

随机值。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(&arr[0] + 1));

随机值。

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));

6,数组名arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr + 0));

arr是首元素的地址==&arr[0],是地址就是4/8个字节。

char* 
    指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节。
    指针变量是用来存放地址的,地址存放需要多大空间,指针变量的大小就是几个字节。
    32位环境下,地址是32个二进制位,需要4个字节,所以指针变量的大小就是4个字节。
    64位环境下,地址是64个二进制位,需要8个字节,所以指针变量的大小就是8个字节。
   不要在门缝里看指针,把指针给看扁了。

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(*arr));

arr是首元素的地址,*arr就是首元素,大小就是1Byte。

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr[1]));

1.

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(&arr));

&arr是数组的地址,sizeof(&arr)就是4/8个字节。

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(&arr + 1));

&arr+1 是跳过数组后的地址,是地址就是4/8个字节。

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(&arr[0] + 1));

第二个元素的地址,是地址就是4/8Byte。

	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));

这个字符串创建的时候末尾必定有一个\0,所以答案是7.

	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr + 0));

arr+0就是第一个元素地址,就是4/8。

	char arr[] = "abcdef";
	printf("%d\n", sizeof(*arr));

 arr既没有单独放到sizeof内部,也没有&,所以是首元素地址,就是a的地址,*arr就是取出a的地址,大小就是一个字节。

	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr[1]));

arr[1]就是第二个元素的地址,所以是1.

	char arr[] = "abcdef";
	printf("%d\n", sizeof(&arr));

&arr就是取出数组的地址,是地址就是4/8个字节。

	char arr[] = "abcdef";
	printf("%d\n", sizeof(&arr + 1));

&arr+1,就是跳过整个数组,但也是地址,就是4/8个字节。

	char arr[] = "abcdef";
	printf("%d\n", sizeof(&arr[0] + 1));

&arr[0]就是取出第一个元素的地址,+1就是第二个元素的地址,就是4/8个字节。

	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));

这个数组名虽然是单独放在strlen内部,但是和sizeof是有区别的,所以代表的是首元素地址,就是a的地址,统计的是\0之前的字符,就是6.

	char arr[] = "abcdef";
	printf("%d\n", strlen(arr + 0));

arr+0代表的是首元素的地址,也是6.

	char arr[] = "abcdef";
	printf("%d\n", strlen(*arr));

对首元素的地址进行解引用,就是a,ASCLL值是97,所以会报错。

	char arr[] = "abcdef";
	printf("%d\n", strlen(arr[1]));

arr[1]是第二个元素,也会报错。

	char arr[] = "abcdef";
	printf("%d\n", strlen(&arr));

取出的是arr的地址,所以是6.

	char arr[] = "abcdef";
	printf("%d\n", strlen(&arr + 1));

跳过整个数组,把\0也跳过了,因为不知道啥时候遇到\0,所以是随机值。

	char arr[] = "abcdef";
	printf("%d\n", strlen(&arr[0] + 1));

取出的是第二个元素的地址,所以是5.

	char* p = "abcdef";
	printf("%d\n", sizeof(p));

 把a的地址交给了p,p是一个指针变量,所以sizeof(p)就是4/8.

	char* p = "abcdef";
	printf("%d\n", sizeof(p + 1));

char*指针加1就向后跳过一个字节,所以p指向了b,也是地址,所以是4/8.

	char* p = "abcdef";
	printf("%d\n", sizeof(*p));

p是char*的指针,所以解引用访问一个字节,就是1.

	char* p = "abcdef";
	printf("%d\n", sizeof(p[0]));

p[0]是第一个元素,所以也是1.

	char* p = "abcdef";
	printf("%d\n", sizeof(&p));

&p也是地址,地址就是4/8个字节。

	char* p = "abcdef";
	printf("%d\n", sizeof(&p + 1));

&p+1也是地址,所以也是4/8个字节。

	char* p = "abcdef";
	printf("%d\n", sizeof(&p[0] + 1));

&p[0]+1就是第二个元素的地址,地址就是4/8个字节。

	char* p = "abcdef";
	printf("%d\n", strlen(p));

遇到\0就停止,所以是6.

	char* p = "abcdef";
	printf("%d\n", strlen(p + 1));

从第二个元素开始,所以是5.

	char* p = "abcdef";
	printf("%d\n", strlen(*p));

*p就是a,97,所以会出错。

	char* p = "abcdef";
	printf("%d\n", strlen(p[0]));

p[0]就是第一个元素,也是97,会出错。

	char* p = "abcdef";
	printf("%d\n", strlen(&p));

&p拿到的是随机值。

	char* p = "abcdef";
	printf("%d\n", strlen(&p + 1));

&p+1也是随机值。

	char* p = "abcdef";
	printf("%d\n", strlen(&p[0] + 1));

&p[0]+1,就是第二个元素的地址,所以是5.


今天的分享到这里就结束啦!谢谢老铁们的阅读,让我们下期再见。

更多推荐

【深度学习实验】线性模型(三):使用Pytorch实现简单线性模型:搭建、构造损失函数、计算损失值

目录一、实验介绍二、实验环境1.配置虚拟环境2.库版本介绍三、实验内容0.导入库1.定义线性模型linear_model2.定义损失函数loss_function3.定义数据4.调用模型5.完整代码一、实验介绍使用Pytorch实现线性模型搭建构造损失函数计算损失值二、实验环境本系列实验使用了PyTorch深度学习框架

针对敏感数据的安全转录服务

即便在新冠肺炎疫情期间,继续保持了最高级别的机密性新冠肺炎疫情带来的各种限制向所有服务提供商提出了挑战,促使提供商们想方设法采取更富想象力的新方法来满足客户的需求。澳鹏采用了一种由两种方案组成的工作机制,服务于客户机密材料的转录,既实现了高度的机密性,又保护了员工的安全。大多数转录服务提供商都会采用基本的安全措施,如员

前端深入理解JavaScript中的WeakMap和WeakSet

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录1.WeakMap和WeakSet概述1.1WeakMap1.2WeakSet2.WeakMap深入解析2.1WeakMap的创建和使用2.2WeakMap和内存管理2.3WeakMap和对象私有数据3.Wea

【Linux】Linux环境配置安装

目录一、双系统(特别不推荐)安装双系统的缺点:安装双系统优点(仅限老手):二、虚拟机+centos7镜像(较为推荐推荐)虚拟机的优点:虚拟机的缺点:​下载centos7的镜像文件下载Ubuntu镜像文件Ubuntu镜像文件下载地址三、云服务器Xshell云服务器共享Xshell删除用户四、powershell一、双系统

前端:运用HTML+CSS+JavaScript实现拼图游戏

前一段时间突然来了一个想法,就是运用前端知识实现一个拼图游戏,但是不知道具体怎样实现。今天,想到既然实现不了现实中我们看到的那种拼块,那么就用正方形来代替吧!效果如下:想到就是当小的图片块放到合适的位置上时,表示拼图完成。文章目录1.前端布局2.js脚本实现小图片块变换位置1.确定随机小图片块的选择2.打乱随机小图片块

阿里云无影云电脑介绍_云办公_使用_价格和优势说明

什么是阿里云无影云电脑?无影云电脑(原云桌面)是一种快速构建、高效管理桌面办公环境,无影云电脑可用于远程办公、多分支机构、安全OA、短期使用、专业制图等使用场景,阿里云百科分享无影云桌面的详细介绍、租用价格、云电脑的优势、使用场景、网络架构、无影云电脑与云服务器的区别以及关于无影云电脑的常见问题解答FAQ:目录阿里云无

以太网ARP测试实验

1.1ARP测试整体框架当上位机发送ARP请求时,FPGA返回ARP应答数据;当按下FPGA的触摸按键时,FPGA发送ARP请求,上位机返回ARP应答数据。PLL时钟对eth_rxc的输入时钟进行相位调整;GMIITORGMI模块负责将双沿(DDR)数据和单沿(SDR)数据之间的转换;ARP顶层模块实现了以太网ARP数

阿里云无影云电脑详细介绍_无影优势价格和使用

什么是阿里云无影云电脑?无影云电脑(原云桌面)是一种快速构建、高效管理桌面办公环境,无影云电脑可用于远程办公、多分支机构、安全OA、短期使用、专业制图等使用场景,阿里云百科分享无影云桌面的详细介绍、租用价格、云电脑的优势、使用场景、网络架构、无影云电脑与云服务器的区别以及关于无影云电脑的常见问题解答FAQ:目录阿里云无

Windows平台Qt6中UTF8与GBK文本编码互相转换、理解文本编码本质

快速答案UTF8转GBKQStringutf8_str="中UTF文";std::stringgbk_str(utf8_str.toLocal8Bit().data());GBK转UTF8std::stringgbk_str_given_by_somewhere="中GBK文";QStringutf8_str=QStr

GMAC & PHY介绍

1.1PHY接口发展(1)MII支持10M/100Mbps,一个接口由14根线组成,它的支持还是比较灵活的,但是有一个缺点是因为它一个端口用的信号线太多。参考芯片:DP83848、DM900A(该芯片内部集成了MAC和PHY接口)。DP83848芯片只支持10、100兆网络通信速度,采用4/5B编码。(2)RMII是简

机器学习第九课--随机森林

一.什么是集成模型对于几乎所有的分类问题(图像识别除外,因为对于图像识别问题,目前深度学习是标配),集成模型很多时候是我们的首选。比如构建一个评分卡系统,业界的标配是GBDT或者XGBoost等集成模型,主要因为它的效果确实好,而且稳定。还有一点是这些模型的可解释性也很好,不像深度学习模型就像个黑盒子。那为什么集成模型

热文推荐