带你学C语言~指针(2)

目录

🏉前言

🚀 数组名的理解

🚀使用指针访问数组

✈一维数组传参的本质

✈冒泡排序

🏆二级指针

🏆指针数组

🏆指针数组模拟二维数组

🎉结束语


🏉前言

上一章,小赵和各位聊了关于C语言指针的开篇章,我们聊了指针最初的模样,但指针这个东西远远不止上一章那样简单,因为我们开辟的内存远远不止这么简单,毕竟还有数组的开辟,还有函数等等这些,包括我们的指针它也是有内存的,那我们究竟该如何去访问它们的地址,如何使用它们的地址内存,这样的地址内存又究竟对我们编写代码会带来什么样革命性的改变,下面小赵讲讲一一为你解答。

🚀 数组名的理解

首先想和各位说的一点就是,其实我们的数组名就是我们这个数组的地址,那这个数组的地址究竟是整个数组的地址呢?还是说是我们的数组首元素的地址,还是说是我们数组结尾的那个地址。这里我们打印出来看看。

 那我们通过我们对我们的数组打印地址,我们发现我们的首元素地址和我们的数组名代表的地址是一样的,那么我们的数组的地址其实也就明白了。

那如果我们对我们的地址名取地址呢?这里我们也可以打印看看。

这里我们惊讶的发现这两个地址,居然是一样的,那这样是否可以说明我们&arr与arr,是一样的?

这里小赵给大家的解释一下,虽然这两个地址是一样的但含义却是极大的不同,这个就和我们之前聊的单位一样,(char int 等),&arr取得是整个数组的地址,所以如果你对它加一,它的单位实际上就是加了一个数组,那另外一个呢?如果只是arr,它其实代表的就是首元素地址,几乎等价于&arr[0],那它加的就是以每一个数组的元素的大小为单位,那么它的加一,就是加到数组的下一个数组的元素。

🚀使用指针访问数组

好了有了上面的知识作为支撑,我们就可以试着去探讨如何用我们的指针和我们的数组相联系了。

int main()
{
	int arr[10] = { 0};
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素
	int* p = arr;//取首元素地址
	for (int i = 0; i < sz; i++)
	{
		scanf("%d", p + i);//给数组每个位置赋值
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d", *(p + i));//打印数组每个位置
	}
	return 0;
}

这个代码小赵要和大家解释一下,这里我们的数组是定义好的,而且我们的指针指向的是首元素的地址,那么我们其实对它加加就相当于从数组首元素向后走,其实还要给大家补个概念,其实我们的指针指向那个地址,那我们的p其实就相当于这个地址,这也就是我们说的指针=地址。那这里就是地址就不用加&,而下面我们打印的是地址里面的东西所以要解地址操作。 

那这时候有人要问了,能不能超过这个数组进行访问了,其实是不行的,因为这里就是一种越界。

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素
	int* p = arr;//取首元素地址
	for (i = 0; i < sz+1; i++)
	{
		printf("%d", *(p + i));//打印数组每个位置
	}
	return 0;
}

 

那这个空间就是未知打印的东西也是未知的。再看这个 

int main()
{
	int arr[3] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素
	int* p = arr;//取首元素地址

	for ( i = 0; i < sz+1; i++)
	{
		scanf("%d", p + i);//给数组每个位置赋值
	}
	for (i = 0; i < sz+1; i++)
	{
		printf("%d", *(p + i));//打印数组每个位置
	}
	return 0;
}

在这里我们就使用了未被定义的空间,那么我们的程序是会报错的。所以这种未被定义的空间是不能使用的,我们指针指向它的时候,其实我们的指针也就相当于野指针。 

✈一维数组传参的本质

#include <stdio.h>
void test(int arr[])
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz2 = %d\n", sz2);
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;

在讲一维数组传参本质前,先给大家看一段代码,这个代码应该就是我们学指针前所用的数组传入函数的方式,但我们其实不知道的是,我们传入的究竟是整个数组的,还是数组的某一个呢?这个代码刚好可以帮我门验证一下这一点。 

在这里我们可以看到我们代码运行后的结果是 10,1这就证明了,我们传入的数组其实只传了一个,那么为什么会这样呢?其实结合小赵这章所聊的内容,我们可以知道我们传入函数的是数组名,那数组名是什么呢?其实就是指针啦,那就证明我们传入函数的其实是个数组首元素指针。那这就好办了,那我们以后数组,不就可以用指针传了吗?而且指针改的是地址,可以让我们在函数中的操作,进入我们的数组中。这里补的一点是,sizeof(arr),计算的是这个所代表的存在里面东西的大小,如果在函数外面,它表示就是整个数组(这一点比较特殊,只有在sizeof这里才代表这个意思。),而我们在函数里面它代表的就是我们传入的首元素的地址,计算的也只有数组首元素的大小。)

void test(int* arr)//参数写成指针形式
{
	printf("%d\n", sizeof(arr) / sizeof(arr[0]));//计算⼀个指针变量的⼤⼩
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;
}

这里我们用指针试试 ,发现结果一样,与我们的猜想是一致的。

✈冒泡排序

有了上面的知识我们就可以试着用指针来编我们的冒泡排序了。

#include <stdio.h>
void BOBBLE(int* a)//优化版冒泡排序
{
	for (int m = 0; m < 4; m++)
	{
		int flag = 1;//判断是否排序完成
		for (int j = 0; j < 4 - m; j++)
		{
			if (a[j] >a[j + 1])//如果a[j+i]都大于arr[j],则证明排序已经完成了
			{
				flag = 0;//如果排序未完成,那么将falg置为零
				int t = a[j + 1];
				a[j + 1] = a[j];
				a[j] = t;
			}
		}
		if (flag == 1)//如果完成,结束循环
			break;

	}

}
int main()
{
	int a[5] = { 0 };
	int i;
	for (i = 0; i < 5; i++)
	{
		scanf("%d", &a[i]);
	}
	BOBBLE(a);
	for (int m = 0; m < 5; m++)
	{
		printf("%d", a[m]);
	}
	return 0;
}

我们会发现有了指针的加入,让我们原本冗杂的长的代码变得更容易看懂,可以将我们的冒泡排序加入到我们的函数中去了,而且由于我们的指针改的其实是数组地址里面的内容,那这就让我们的数组可以真正的改变了(这个有点类似(1)的交换 )。

同时我们对我们的之前的冒泡排序进行了优化,让我们的代码可以不用执行的很复杂,大大加快代码了运行速度。

🏆二级指针

那好了数组解决了吧,那我们一级指针的地址咋说,谁能来管管呢?就这样我们的二级指针诞生了。

当然,我们得先看看我们指针是否有地址,看看它的地址。

我们看到我们指针指向的地址,与我们指针本身的地址是不一样的,这也就间接证明了,我们的指针其实是有自己独立的地址。

那我们的二级指针是什么样的,其实也蛮简单的,我们可以用上一章的思想解决,打开一个空间要一个钥匙,那我们现在打开两个呢?那不就是两个吗。

int main()
{
	int a = 0;
	int* p = &a;
	int** b = &p;
	printf("%p\n", &a);
	printf("%p\n", p);
	printf("%p\n", &p);
	printf("%p", b);
}

那我怎么才能访问到里面的a能其实也就是开两次锁。

 

🏆指针数组

那紧接着下来就是,我们有木有这样一个数组里面存的是指针呢?那其实也是有的。我们之前聊到,数组前面的那个就是它里面的元素类型,那我们只要将前面写成int*,char*等就可以了。

int main()
{
	int a = 0;
	int* p = &a;
	int* b[1] = { p };//指针数组
	printf("%d", *(b[0]));
}

🏆指针数组模拟二维数组

那一维数组聊完了,那二维数组呢?这里就不得不提小赵前面的概念了,二维数组里面每个元素其实就是一维数组,那既然是一维数组,我们是不是可以试着用一维数组的首元素地址代替呢?然后再对这个元素地址进行解指针操作,加一加二等,这样是不是就让指针数组模拟二维数组的操作完成了。 

#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* parr[3] = { arr1, arr2, arr3 };//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);//这里要解释一下,其实我们a[1]=*(a+1)的操作,那我们这里的arr[1][2]其实也就等于*(arr[1]+2)的操作。
		}
		printf("\n");
	}
	
	return 0;
}

这里要和家人们 解释的其实就是我批注的那一块,我们的arr[]这个操作其实就是在对我们的arr进行解指针的操作,而括号里面的数就是我们arr+数字,地址的移动,然后进行解指针。 

🎉结束语

好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵有什么地方说得不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,小赵及时改正,感谢大家支持!!!

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

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

相关文章

torch.gather(...)

1. Abstract 对于 pytorch 中的函数 torch.gather(input, # (Tensor) the source tensordim, # (int) the axis along which to indexindex, # (LongTensor) the indices of elements to gather*,sparse_gradFalse,outNone ) → Tensor有点绕&#xff0c;很多博客画各…

Webpack安装及使用

win系统 全局安装Webpack及使用 前提&#xff1a;使用Webpack必须安装node环境&#xff0c;建议使用nvm管理node版本。 1&#xff1a;查看自己电脑是否安装了node 2&#xff1a;npm install webpack版本号 -g 3&#xff1a;npm install webpack-cli -g -g:表示全局安装 4&…

ElasticSearch单机或集群未授权访问漏洞

漏洞处理方法&#xff1a; 1、可以使用系统防火墙 来做限制只允许ES集群和Server节点的IP来访问漏洞节点的9200端口&#xff0c;其他的全部拒绝。 2、在ES节点上设置用户密码 漏洞现象&#xff1a;直接访问9200端口不需要密码验证 修复过程 2.1 生成认证文件 必须要生成…

力扣225. 用队列实现栈【附进阶版】

文章目录 力扣225. 用队列实现栈示例思路及其实现两个队列模拟栈一个队列模拟栈 力扣225. 用队列实现栈 示例 思路及其实现 两个队列模拟栈 队列是先进先出的规则&#xff0c;把一个队列中的数据导入另一个队列中&#xff0c;数据的顺序并没有变&#xff0c;并没有变成先进后…

【转载】【Unity】WebSocket通信

1 前言 Unity客户端常用的与服务器通信的方式有socket、http、webSocket。本文主要实现一个简单的WebSocket通信案例&#xff0c;包含客户端、服务器&#xff0c;实现了两端的通信以及客户端向服务器发送关闭连接请求的功能。实现上没有使用Unity相关插件&#xff0c;使用的就是…

【经典LeetCode算法题目专栏分类】【第5期】贪心算法:分发饼干、跳跃游戏、模拟行走机器人

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 分发饼干 class Solutio…

换热站数字孪生 | 图扑智慧供热 3D 可视化

换热站作为供热系统不可或缺的一部分&#xff0c;其能源消耗对城市环保至关重要。在双碳目标下&#xff0c;供热企业可通过搭建智慧供热系统&#xff0c;实现供热方式的低碳、高效、智能化&#xff0c;从而减少碳排放和能源浪费。通过应用物联网、大数据等高新技术&#xff0c;…

MongoDB中的关系

本文主要介绍MongoDB中的关系。 目录 MongoDB的关系嵌入关系引用关系 MongoDB的关系 MongoDB是一个非关系型数据库&#xff0c;它使用了键值对的方式来存储数据。因此&#xff0c;MongoDB没有像传统关系型数据库中那样的表、行和列的概念。相反&#xff0c;MongoDB中的关系是通…

美颜SDK是什么?视频美颜SDK在直播平台中的集成与接入教程详解

当下&#xff0c;主播们追求更加自然、精致的外观&#xff0c;而观众也期待在屏幕前欣赏到更为清晰、美丽的画面。为了满足这一需求&#xff0c;美颜SDK应运而生&#xff0c;成为直播平台的重要利器之一。 一、什么是美颜SDK&#xff1f; 通过美颜SDK&#xff0c;开发者可以…

docker在线安装minio

1、下载最新minio docker pull minio/minio 2、在宿主机创建 /usr/local/data/miniodocker/config 和 /usr/local/data/miniodocker/data,执行docker命令 docker run -p 9000:9000 -p 9090:9090 --name minio -d --restartalways -e MINIO_ACCESS_KEYminio -e MINIO_SECRET_K…

数据结构--图

树具有灵活性&#xff0c;并且存在许多不同的树的应用&#xff0c;但是就树本身而言有一定的局限性&#xff0c;树只能表示层次关系&#xff0c;比如父子关系。而其他的比如兄弟关系只能够间接表示。 推广--- 图 图形结构中&#xff0c;数据元素之间的关系是任意的。 一、图…

Shell编程基础 – C语言风格的Bash for循环

Shell编程基础 – C语言风格的Bash for循环 Shell Programming Essentials - C Style For Loop in Bash By JacksonML 循环是编程语言的基本概念之一&#xff0c;同样也是Bash编程的核心。当用户需要一遍又一遍地运行一系列命令直到达到特定条件时&#xff0c;例如&#xff1…

输电线路定位:精确导航,确保电力传输安全

在现代社会中&#xff0c;电力作为生活的基石&#xff0c;其安全稳定运行至关重要。而输电线路作为电力传输的重要通道&#xff0c;其故障定位和修复显得尤为重要。恒峰智慧科技将为您介绍一种采用分布式行波测量技术的输电线路定位方法&#xff0c;以提高故障定位精度&#xf…

06-部署knative-eventing

环境要求 For prototyping purposes 单节点的Kubernetes集群&#xff0c;有2个可用的CPU核心&#xff0c;以及4g内存&#xff1b; For production purposes 单节点的Kubernetes集群&#xff0c;需要至少有6个CPU核心、6G内存和30G磁盘空间多节点的Kubernetes集群中&#xff0c;…

电影小镇智慧旅游项目技术方案:PPT全文111页,附下载

关键词&#xff1a;智慧旅游项目平台&#xff0c;智慧文旅建设&#xff0c;智慧城市建设&#xff0c;智慧文旅解决方案&#xff0c;智慧旅游技术应用&#xff0c;智慧旅游典型方案&#xff0c;智慧旅游景区方案&#xff0c;智慧旅游发展规划 一、智慧旅游的起源 智慧地球是IB…

windows 安装jenkins

下载jenkins 官方下载地址&#xff1a;Jenkins 的安装和设置 清华源下载地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/jenkins/windows-stable/ 最新支持java8的版本时2.346.1版本&#xff0c;在清华源中找不到&#xff0c;在官网中没找到windows的下载历史&#xff…

MySQL数据库 约束

目录 约束概述 外键约束 添加外键 删除外键 删除/更新行为 约束概述 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确、有效性和完整性。 分类: 注意&#xff1a;约束是作用于表中字段上…

java继承

1.为什么需要继承 我们编写了两个类,一个是Puppil类(小学生),一个是Graduate(大学生),问题:两个类的属性和方法有很多是相同的,怎么办&#xff1f; 把共有的属性和方法抽离出来: 父类&#xff1a; package com.hspedu.extends01;//父类,是Pupil和Graduate的父类 public cla…

50ms时延工业相机

华睿工业相机A3504CG000 参数配置&#xff1a; 相机端到端理论时延&#xff1a;80ms 厂家同步信息&#xff0c;此款设备帧率上线23fps&#xff0c;单帧时延&#xff1a;43.48ms&#xff0c;按照一图缓存加上传输显示的话&#xff0c;厂家预估时延在&#xff1a;80ms 厂家还有…

AXure的情景交互

目录 导语&#xff1a; 1.erp多样性登录界面 2.主页跳转 3.省级联动​编辑 4. 下拉加载 导语&#xff1a; Axure是一种流行的原型设计工具&#xff0c;可以用来创建网站和应用程序的交互原型。通过Axure&#xff0c;设计师可以创建情景交互&#xff0c;以展示用户与系统的交…