脆皮之“指针和数组的关系”

文章目录

  • 1. 数组名的理解
  • 2. 使用指针访问数组
  • 3. 一维数组传参的本质
  • 4. 冒泡排序
  • 5. 二级指针
  • 6. 指针数组
  • 7. 指针数组模拟二维数组

hello,大家好呀,窝是脆皮炸鸡。这一期是关于数组和指针的,我觉得并不是很难,但是我觉着下一期可能稍难,大家有什么不懂的可以在评论区询问,加油,开始学习叭!!!

Ps:点赞收藏加关注,追番永远不迷路。

在这里插入图片描述

1. 数组名的理解

大家是否记得在上一期的指针内容里,我曾说过:数组名是数组首元素的地址。但是有两个例外,这两个例外是:sizeof(数组名),&数组名。除了这两个情况,其余情况下,数组名就是数组首元素的地址。

----------------------------------------------- 1 -----------------------------------------------

举个例子:sizeof(数组名)

	int arr[7] = { 0 };
	printf("sizeof(arr)=%d\n", sizeof(arr)); //这里sizeof是在计算arr的大小

大家知道此处打印的结果是什么吗,是28。如果按之前说的”数组名都是数组首元素的地址“,那首元素的地址占四个字节,为什么输出是28呢?

因为,此处数组名代表的并不是数组首元素地址。sizeof(数组名),其中的数组名表示的是整个数组,计算的是整个数组的大小,单位是字节。我们再回头看,这个数组共7个整数,一个整数占4个字节,所以一共占4*7=28个字节

----------------------------------------------- 2 -----------------------------------------------

举个例子:&数组名

int arr1[7] = { 0 };
printf("arr1    =%d\n", arr1);  //这里的数组名在两种情况外,是数组首元素地址
printf("&arr1   =%d\n", &arr1);  //整个数组的地址

事实上,打印出来的两个结果是相同的,都是数组首元素地址。

这是合乎情理的,arr1(不是那两种特殊情况)代表的是数组首元素地址;&arr1代表的是数组的地址。从下图可以看出,两者的起始地址一样。

在这里插入图片描述

在此处我们看不出它俩的差异。

大家是否记得之前的一句话“指针类型决定了指针的差异”。整型指针+1,跳过4个字节;字符指针+1,跳过1个字节。那数组指针+1,应当是跳过整个数组的。

printf("arr1    =%d\n", arr1);  //数组名
printf("arr1+1  =%d\n", arr1+1);  

printf("&arr1   =%d\n", &arr1);  //整个数组的地址
printf("&arr1+1 =%d\n", &arr1+1);  

在这里插入图片描述

(最后两个:44到48,+4;44到72,+28)图上显示:数组首元素地址+1,跳过4个字节;数组地址+1,跳过28个字节,即跳过整个数组。

总结:&数组名:这里的数组名表示整个数组,取出的是整个数组的地址

2. 使用指针访问数组

在此强调一下:

  • 数组是数组(是一块连续的空间,可存放一个或多个数组)
  • 指针是指针(用于存放地址的变量)
  • 它俩不是一回事,但是可以使用指针来访问地址。

为什么可以用指针来访问数组?
1.数组在内存中是连续存放的(指针+1是很方便遍历数组的)
2.指针的元素很方便遍历数组,取出数组内容。

我注释掉的方法是以前学习的方法,没有注释的方法是使用指针来访问的

int main()
{
	int arr[6] = { 0 };
	//输入
	int index = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (index = 0; index < sz; index++)
	{
		/*scanf("%d", &arr[index]); */
		scanf("%d", arr + index); 
		//arr是首元素地址,+1,跳过(4个字节)一个整数。
		//arr+index,第一次循环就是arr+0,即首元素地址;第二次循环,arr+1,即下一个整数
	}

	//输出
	for (index = 0; index < sz; index++)
	{
		/*printf("%d ", arr[index]); */
		printf("%d ", *(arr + index));
		//将每个元素的地址解引用,即可找到地址所指向的对象
	}
	return 0;
}

将输出的两种方式对比,arr[index] 和 * (arr+index) 它俩是完全等价滴。【其实,编译器在看到arr[index]是转化成 *(arr+index)来看的】

3. 一维数组传参的本质

数组是可以通过实参传递给函数的,现在我们来讨论一下数组传参的本质。大家先思考一下,假设我们要将数组int arr[7]传过去,当时我们说过,数组传的是数组名(实参),形参用什么来接收嘞?int arr [ ](数组大小可忽略)

在这里插入图片描述
第一种我们直接计算,结果是正确的;但在函数里却错了,为什么呢?

我们仔细看一下,print(arr),不是那两种例外情况,所以arr是数组首元素地址,要是地址的话,我们应该用int* arr接收的,然后(首元素地址4个字节) / (首元素地址4个字节)=1。到这我们就找到问题了。【之前我们之所以用int arr[ ]接收,只是为了方便理解,当时还没有学指针。】数组传参的时候,形参可以写成数组(易理解,但其实本质还是指针),也可以写指针变量。

总结:从中可以看出,数组传参的本质,传递的是首元素的地址,即便我们将形参写成数组的形式,它本质上也是一个指针变量。

那如果我们就是想将数组传过去呢?这时,我们不仅需要将数组名传过去,还需要将数组元素个数sz传过去,接下来举例:先将数组传给函数,再通过函数打印数组。

void print(int* arr, int sz)
{
	int index = 0;
	for (index = 0; index < sz; index++)
	{
		printf("%d ", *(arr + index));
	}
}
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);  将数组大小也传过去
	return 0;
}

4. 冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较。不满足顺序就交换,满足顺序就比较下一对。

以 “将乱序的数字变为有序的数字(升序/降序) ” 为例进行分析。(我这里是升序)

  1. 首先输入数列元素。
  2. 然后冒泡排序,确定冒泡排序需要几趟(index)
  3. 在每一趟里,又要进行几次比较。把什么进行比较呢?将【第一个(下标为0的元素)】和【第二个元素(下标为1的元素)】比较,第二个和第三个比较…(j),总是将arr[j]和arr[j+1]进行比较
  4. 最后打印出来。

先展示第一趟冒泡排序:看完图片之后发现,一趟冒泡排序只将一个数字放在它本应该在的位置。

在这里插入图片描述

第二趟冒号排序:
在这里插入图片描述

之后的和这类似,现在思考一下:

  1. 我有10个元素,需要几趟冒号排序呢?9趟(你想想,9个数字都已经在正确的位置了,最后一个元素的位置肯定正确呀)所以,sz个元素,sz-1趟
  2. 10个元素,第一趟进行9次比较,第二趟进行8次比较,所以:sz个元素,第一次进行sz-1次比较,第二次进行sz-2次比较…那么在循环里,如何描述几次比较呢?sz-1写成sz-1-index(第一趟index是0,这里就是sz-1),sz-2就是sz-1-1(因为此时index是1,所以是sz-1-1)
void input(int* arr, int sz);  //用于输入数字
void bubble(int* arr, int sz);  //用于冒泡排序
void print(int* arr, int sz);  //用于打印排序好的数组

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//输入数字
	input(arr, sz);
	//将乱序的数字排序
	bubble(arr, sz);
	//最后将升序的数字打印出来
	print(arr, sz);
}
void input(int* arr, int sz)  //用于输入数字
{
	int index = 0;
	for (index = 0; index < sz; index++)
	{
		scanf("%d", arr + index);
	}
}
void bubble(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 = 0;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
void print(int* arr, int sz)  //用于打印排序好的数组
{
	int q = 0;
	for (q = 0; q < sz; q++)
	{
		printf("%d ", *(arr + q));
	}
}

5. 二级指针

我们之前学习的都是一级指针,比如:int*,char*,float*
一级指针是用来存放(普通变量)的地址
二级指针是用来存放(一级指针变量)的地址
int* ,int**

	int a = 90;
	int* pa = &a;  //pa是一级指针变量

这里,我将a的地址放在指针变量pa里,但是大家想想,指针变量也是变量,它也有自己的地址,我们又可以将指针变量的地址存储起来放在ppa。并且我们可以通过ppa找到最开始的普通变量a:printf(“%d”,**ppa); 也可以通过ppa改变a的值。

int*      * ppa = &pa;  //ppa是二级指针变量
        //这个*说明ppa是指针变量
//int*说明ppa指向的pa的类型是int*

之前我们可以通过一级指针访问数组元素,但是在这里说明,二级指针和二维数组没有任何关系。

6. 指针数组

大家可以将指针当作定语,数组是主语。所以指针数组本质上是数组。

字符数组:char arr[10]; 存放字符的数组
整型数组:int arr[10]; 存放整型的数组
指针数组:它是一个存放指针的数组,它的每个元素都是指针类型(int或者char等等)。
char* arr[10] :存放字符指针的数组。int* arr[10]:存放整型指针的数组。

它的用处:(1)可以将相同类型的变量的地址存储在数组中,不用再一个一个的存储
(2)存储之后又可以遍历数组(元素是地址)再解引用打印最初的变量

	int a = 90;
	int b = 41;
	int c = 50;
	int* arr[3] = { &a,&b,&c };
	int index = 0;
	for (index = 0; index < 3; index++)
	{
		printf("%d ", *(arr[index]));
	}

7. 指针数组模拟二维数组

我先创建3个一维数组,再用过int*将它们关联起来(可以达到用1个一维数组维护3个一维数组的效果)

这是模拟出来的,不是真的二维数组

int arr1[4] = { 1,2,3,4 };
int arr2[4] = { 1,1,2,4, };
int arr3[4] = { 2,4,6,8 };
int* ARR[3] = { arr1,arr2,arr3 };
//这里的arr1并不是那两种例外,所以是数组首元素的地址
//我们想通过首元素地址然后遍历这个数组的所有元素
int i = 0;
for (i = 0; i < 3; i++)
{
	int j = 0;
	for (j = 0; j < 4; j++)
	{
		printf("%d ", ARR[i][j]);
		//ARR[i]等价于*(ARR+i) ,ARR[i][j]等价于*(*(ARR+I)+j)
		}
	}
	printf("\n");
}

在这里插入图片描述

ARR [i] 是访问ARR数组的元素,ARR [i] 找到的数组元素指向了整型一维数组,ARR[i][j]就是整型一维数组中的元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/612322.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最新版Ceph( Reef版本)块存储简单对接k8s

当前ceph 你的ceph集群上执行 1.创建名为k8s-rbd 的存储池 ceph osd pool create k8s-rbd 64 642.初始化 rbd pool init k8s-rbd3 创建k8s访问块设备的认证用户 ceph auth get-or-create client.kubernetes mon profile rbd osd profile rbd poolk8s-rbd部署 ceph-rbd-csi c…

如何用 OceanBase做业务开发——【DBA从入门到实践】第六期

当应用一款新的数据库时&#xff0c;除了基础的安装部署步骤&#xff0c;掌握其应用开发方法才是实现数据库价值的关键。为此&#xff0c;我们特别安排了5月15日&#xff08;周三&#xff09;的《DBA 从入门到实践》第六期课程——本次课程将带大家了解OceanBase数据库的开发流…

学习Java的日子 Day45 HTML常用的标签

Day45 HTML 1.掌握常用的标签 1.1 标题标签 h1-h6 <h1>一级标签</h1> <h2>二级标签</h2> <h3>三级标签</h3> <h4>四级标签</h4> <h5>五级标签</h5> <h6>六级标签</h6> 显示特点&#xff1a; * 文字…

论文解读--------FedMut: Generalized Federated Learning via Stochastic Mutation

动机 Many previous works observed that the well-generalized solutions are located in flat areas rather than sharp areas of the loss landscapes. 通常&#xff0c;由于每个本地模型的任务是相同的&#xff0c;因此每个客户端的损失情况仍然相似。直观上&#xff0c;…

鸿蒙内核源码分析(文件句柄篇) | 你为什么叫句柄

句柄 | handle int open(const char* pathname,int flags); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int close(int fd);只要写过应用程序代码操作过文件不会陌生这几个函数,文件操作的几个关键步骤嘛,跟把大…

react18【实战】tab切换,纯前端列表排序(含 lodash 和 classnames 的安装和使用)

技术要点 动态样式 className{tabItem ${currentType item.value && "active"}}安装 lodash npm i --save lodash使用 lodash 对对象数组排序&#xff08;不会改变源数组&#xff09; _.orderBy(dataList, "readNum", "desc")src\De…

WireShark对tcp通信数据的抓包

一、抓包准备工作 安装wireshark sudo apt update sudo apt install wireshark 运行 二、WireShark工具面板分析 上图中所显示的信息从上到下分布在 3 个面板中&#xff0c;每个面板包含的信息含义如下&#xff1a; Packet List 面板&#xff1a;显示 Wireshark 捕获到的所…

【项目实战】使用Github pages、Hexo如何10分钟内快速生成个人博客网站

文章目录 一.准备工作1.安装git2.安装node安装 cnpm 3.使用 GitHub 创建仓库&#xff0c;并配置 GitHub Pages0.Github Pages是什么1. 在 GitHub 上创建一个新仓库2. 创建您的静态网站3. 启用 GitHub Pages4. 等待构建完成5. 访问您的网站 二. Hexo1.什么是Hexo2.安装Hexo1. 安…

【核武器】2024 年美国核武器-20240507

2024年5月7日,《原子科学家公报》发布了最新版的2024美国核武器手册 Hans M. Kristensen, Matt Korda, Eliana Johns, and Mackenzie Knight, United States nuclear weapons, 2024, Bulletin of the Atomic Scientists, 80:3, 182-208, DOI: https://doi.org/10.1080/00963…

Vue面试经验2

Vue 你说你在vue项目中实现了自定义指令&#xff0c;如何实现 全局指令在main.js入口文件中实现 使用方法&#xff1a;v-指令名称 每个钩子函数都有两个参数&#xff08;ele,obj&#xff09; ele:绑定指令的元素 obj:指令的一些信息&#xff08;比如绑定指令的值&#xff0c…

OpenCV中的模块:点云配准

点云配准是点云相关的经典应用之一。配准的目的是估计两个点云之间位姿关系从而完成两者对应点之间的对齐/对应,因而在英文中又叫“align”、“correspondence”。笔者曾经是基于OpenCV进行三维重建的,并且从事过基于深度学习的6DoF位置估计等工作。在这些工作中,除了重建点…

docker compose kafka集群部署

kafka集群部署 目录 部署zookeeper准备工作2、部署kafka准备工作3、编辑docker-compose.yml文件4、启动服务5、测试kafka6、web监控管理 部署zookeeper准备工作 mkdir data/zookeeper-{1,2,3}/{data,datalog,logs,conf} -p cat >data/zookeeper-1/conf/zoo.cfg<<EOF…

基于STM32F401RET6智能锁项目(使用库函数点灯、按键)

点灯硬件原理图 1、首先&#xff0c;我们查看一下原理图&#xff0c;找到相对应的GPIO口 LED_R低电平导通&#xff0c;LED4亮&#xff0c;所以LED_R的GPIO口需要配置一个低电平才能亮&#xff1b; LED_G低电平导通&#xff0c;LED3亮&#xff0c;所以LED_R的GPIO口需要配置一…

[C++核心编程-06]----C++类和对象之对象模型和this指针

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

VTK 数据类型:规则网格

VTK 数据类型&#xff1a;规则网格 VTK 数据类型&#xff1a;规则网格分类三种规则网格需要的设置实例 VTK 数据类型&#xff1a;规则网格 分类 VTK 有 3 种规则网格&#xff1a; vtkImageData&#xff1a;几何结构和拓扑结构都是规则的。vtkRectilinearGrid&#xff1a;几何…

使用2G内存求20亿个数字中出现次数最多的N个

又是一个TOP -N的题目 我看了一下CSDN上大多数人的回答和GPT说的差不多&#xff0c;都是说使用哈希之类的&#xff1b; 我今天说一下我的解法&#xff0c;首先说一下不太快的基础解法 20亿数字使用uint32需要80GB&#xff0c; &#xff08;1&#xff09;分为40块读取&#…

云粒智慧实时数仓的架构演进分享:OceanBase + Flink CDC

4月20日&#xff0c;在2024 OceanBase开发者大会上&#xff0c;云粒智慧的高级技术专家付大伟&#xff0c;分享了云粒智慧实时数仓的构建历程。他讲述了如何在传统数仓技术框架下的相关努力后&#xff0c;选择了OceanBase Flink CDC架构的实时数仓演进历程。 业务背景及挑战 …

【C#进阶】简单数据结构类

简单数据结构类 文章目录 1、Arraylist1、ArrayList的本质2、声明3、增删查改4、装箱拆箱思考 背包售卖 2、Stack1、Stack的本质2、声明3、增取查改4、遍历思考 计算一个数的二进制 3、Queue1、Queue的本质2、声明3、增取查改4、遍历思考 每隔一段时间打印一条消息 4、Hashtab…

赣红孵联合卫东街道未保站开展未成年人保护法散落在每个角落活动

为进一步提高家长的法治意识&#xff0c;依法保障未成年人的合法权益&#xff0c;全力构建安全和谐文明家庭&#xff0c;5月8日&#xff0c;赣红孵社会组织培育中心联合卫东街道未成年人保护站在在南师附小红谷滩校区实验小学开展“未成年人保护法散落在每个角落”未成年人普法…

无列名注入

在进行sql注入时&#xff0c;一般都是使用 information_schema 库来获取表名与列名&#xff0c;因此有一种场景是传入参数时会将 information_schema 过滤 在这种情况下&#xff0c;由于 information_schema 无法使用&#xff0c;我们无法获取表名与列名。 表名获取方式 Inn…