深入理解并打败C语言难关之一————指针(3)

前言:

  昨天把指针最为基础的内容讲完了,并且详细说明了传值调用和传址调用的区别(这次我也是做到了每日一更,感觉有好多想写的但是没有写完),下面不多废话,下面进入本文想要说的内容

目录:

1.数组名的理解

1.1数组名代表着什么

1.2.数组名的几种特殊情况

2.使用指针访问数组

2.1指针如何访问数组

3.一维数组传参的本质

4.冒泡排序算法的实现(先预热一下,这个太重要我写在下一篇了)

5.二级指针相关知识点

5.1二级指针是什么

5.2二级指针中的解引用操作

6.指针数组相关知识点

6.1指针数组

6.2利用指针数组模拟二维数组

正文:

1.数组名的理解

1,1数组名是什么

  其实数组名我在之前就解释过了,现在我们正式的来介绍它,数组名是数组首元素的地址,通过代码的撰写就可以看出来二者是一样的,代码以及展示图如下:

#include<stdio.h>
int main()
{
    int arr[10] = {0};
    int *p = &arr[0];
    printf("%p",arr);
    printf("%p",p);
    return 0;
}

  在说上面之前,我想起来我忘记说%p这个占位符的作用了,这个占位符是代表着地址的占位符,想要打印地址就需要它,不难看出,上面两个地址打印出来的结果是一样的,所以可以更好的理解数组名就是数组首元素的地址,不过,这也不是绝对的,下面一种特殊情况数组名就不是数组第一个元素的地址。

1.2数组名的几种特殊情况

  1.2.1sizeof(arr)

  我们知道,sizeof运算符是用来计算长度的,上面讲了,arr指的是数组第一个元素的地址,按道理来讲,我们计算arr的长度应该就是第一个元素的长度即四个字节,但是看见下面的代码和打印结果:

#include<stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%zd",sizeof(arr));
    return 0;
}

  我们不难看出,这里打印的结果是40个字节,为什么会打印出40呢?因为这是arr整体的元素个数,所以此时sizeof(arr)并不是第一个元素的长度,而是整个元素的长度,这个一定要记住,在某些企业的笔试题中或者正常的考试题中可能会出一个类似这样的题来迷惑你,所以我们要记住这个特殊情况! 不要出错,不要出错,不要出错!

  1.2.2&arr

  上面小编说过,arr是数组名,代表着数组第一个元素的地址,那么我们如果直接对arr去地址呢?这种情况我们先通过代码打印看一下:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr);
	return 0;
}

  看到这里可能有些读者朋友会想,这三个的地址不都是相同的吗?是不是&arr就是数组第一个元素的地址呢?当然,如果真一样的话,我写这个小标题就一点意义都没有了,先不要着急,我们再看一下下面这个代码就可以知道&arr到底指的是什么了:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr + 1);
	printf("%p\n", &arr[1]);
	printf("%p\n", &arr + 1);
	return 0;
}

  这个时候就可以很清楚的发现三者的不同了,前两个肯定是一样的(前面的知识点的运用),而第三个,经过我们的计算,发现比前两个多了36个字节的内存空间 ,这是为什么呢?我也不卖关子了,其实取地址arr取到的是整个数组的地址,所以我们在进行加1操作的时候,其余都是加了一个字节,而&arr是跳跃了一个完全的数组,从而我们在想要取到地址的时候,一定要知晓取到的应该是一个完整的数组地址,不过刚开始指向的是数组第一个元素的地址罢了,在进行加减的时候一定要小心。

小结:

  我们在使用数组名的时候一定要注意这两个特殊情况,除了这两个特殊情况意外,其余的数组名都是代表首元素的地址

2.使用指针去访问数组

2.1指针如何访问数组

  其实用指针数组我在上一节课的代码中就已经实现了,以后我还是得改掉超前讲的坏毛病,不多废话,我们知道数组名就是数组第一个元素的地址,所以我们在使用输入函数的时候便可以用数组名来替代&arr[i]类似这样的写法,这样可以减少我们在使用scanf函数的时候忘记运用取地址符的尴尬情况(小编就时常忘记写这个,我还抱怨是编译器的问题,😅),在打印的时候我们就可以利用解引用操作符来实现操作,光说不练等于白说,下面来进行一个久违的环节,小小的例题

例1.利用指针访问数组随机打印五个数


#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		scanf("%d", arr + i);
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(arr + i));  //在这里arr[i] == *(arr + i),甚至还可以写成[i]arr,我们在这里可以看出[]仅仅就是一个运算符罢了,不过为了视觉效果所以才这么用的
	}
	return 0;
}

  可以看出这个功效和我们正常使用一位数组是一样的,不过正如我前面所说的,这样写可以避免一些错误的出现,这也让我们之后写数组的方式变得更多了 ,一定要合理运用(scanf函数会时时常忘记写的读者朋友们可以这么写呦~)。

3.一维数组传参的本质

  数组我们是知道的,函数我们也是知道的,但是我们之前在利用函数传数组的参数的时候,我们也是传参的数组名,当时我们并没有考虑到数组名到底是干什么的,也不知道数组名就是数组第一个元素的地址,现在根据我们前面讲过的知识,下面小编出一个小小的题目,我们可以不可以通过函数来计算数组元素的个数呢?读者朋友们可以先思考一下,下面是相关代码:

#include<stdio.h>
void ceshi(int arr[10])
{
	printf("%zd", sizeof(arr));
}
int main()
{
	int arr[10] = { 0 };
	ceshi(arr);
	return 0;
}

   可以看出打印出来的结果是8(我这是在X64环境下写的),不难看出,此时打印的数组元素的地址的大小,并没有打印出整个元素的字节长度,原因很简单,我们知道arr是数组名,代表的是第一个元素的地址,所以我们传过去的是地址,计算长度的时候自然就是计算地址的长度了!所以我们每次想要传数组的时候,需要把数组的个数也传递过去。对于数组传参,其实是有两种形式的,下面来展示一下:

  

#include<stdio.h>
void ceshi1(int arr[10])
{
	//我就不写内容了,看外观别看内容
}
void ceshi2(int* arr)
{
	//这两个是一样的,小编比较喜欢写第一个的形式(说实在的这个形式在我复习的时候我才记着有)
}
int main()
{
	int arr[10] = { 0 };
	ceshi1(arr);
	ceshi2(arr);
	return 0;
}

  正如我在代码页所写的,两种形式均可,可以按着自己习惯来进行书写,第二个可以更好的展示为什么字节数是4 / 8个,因为这本来就是计算地址的长度!

小结:

  对于这部分的知识点,大家一定要好好掌握(客套话),下面进入二级指针(冒泡排序我觉着单独出一篇来写去,感觉写在中间有点显的它不重要) 。

4.冒牌排序算法的实现(下一篇见喽)

5.二级指针相关知识点

5.1.二级指针是什么

  我们知道,指针变量也是变量,同样也是有地址的,对于普通变量地址的存放是在指针里面,那么指针变量的地址应该存放到哪里呢,显然易见,它会放到二级指针里面,所以用大白话来讲,二级指针就是指针的指针,对于它如何进行初始化,下面献上代码:​​​​​​​

#include<stdio.h>
int main()
{
	int a = 20;
	int* p = &a;
	int** m = &p;   //可以这么记,有多少个*就代表着几级指针在初始话的时候
	return 0;
}

  正如我上问所说,经过我的观察,我发现*数决定着几级指针,其实二级指针目前我也没有设计很多,大多数题目设计到的都是一级指针,二级指针先知道大概就行,小编觉着这个在后期(数据结构,我还没学不知道大概)使用的应该更广一点。 

5.2.二级指针的解引用操作

  我们知道,在一级指针的时候进行解引用操作的时候,会直接取到这个数的本身,所以我们类比二级指针,在二级指针进行解引用的时候,会直接对应到一级指针的内容,也就是一级指针对应元素的地址,详情的代码如下:

#include<stdio.h>
int main()
{
	int a = 20;
	int* p = &a;
	int** m = &p;
	printf("%p\n", p);
	printf("%p", *m);
	return 0;
}

 

  从上面可以很显然的看出二者是相同的,这更加的证实了我上面所说,对二级指针进行第一次解引用的时候,得出的是一级指针的详情内容,划重点(第一次),所以有一必有二,二级指针也可以进行二次解引用,试想一下,第一次解引用的时候取到的是一级指针,对二级指针进行解引用的时候就是对一级指针再次进行解引用,得到了数的本身,很有趣吧,知识都是环环相扣的,下面是代码展示:

#include<stdio.h>
int main()
{
	int a = 20;
	int* p = &a;
	int** m = &p;
	**m = 12;
	printf("%d\n", a);
	printf("%d\n", **m);
	return 0;
}

 

  上面的代码很好的证实了我刚才说的正确性,所以二级指针是可以进行二次解引用的,当然,除了二级指针,还有三级指针,四级指针等等 ,不过这些都不经常使用,所以,指针记住一级指针=和二级指针就好,我们记住对我们有用的,记太多是容易遗忘的。

小结:

  二级指针的知识已经全部都讲完了,希望读者朋友们好好的理解,下面进入最后一小节喽!

6.指针数组相关知识点 

6.1指针数组

  在讲到这个之前,记住这是指针数组,一定要跟后期的数组指针区分开, 这俩真容易弄混。好了,进入正题,指针数组这个名字,可以类比记忆,类似整型数组,浮点型数组,顾名思义,这是个存放指针的数组,数组中的每一个元素都是指针(地址),下面来介绍一下它是如何进行初始化的:

int * arr[5] = {0};

  int代表的是类型,这一个不唯一,我只是当作例子展示一下,也可以是float,double等等,* 代表的是指针类型,arr是指的名字,arr[5]代表着存了五个地址,一定要记住它的写法,后期有很多指针类型等着我们记,我们一定不要搞混,这便是指针数组的初始化,下面可以进入一个小小的案例:用指针数组来模拟实现二维数组(小重点)! 

 

6.2.利用指针数组模拟二维数组 

  二维数组我们都知道(这部分我没有出博客,因为我有点懒),我们可以通过我们刚刚了解到的指针数组来进行模拟实现二维数组,下面先呈上代码供各位观看(代码下面是讲解

#include<stdio.h>
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 1,2,3,4,5 };
	int arr3[5] = { 1,2,3,4,5 };
	int* arr[] = { arr1,arr2,arr3 };   //存放的都是数组第一个元素的地址,存放地址的叫做指针数组
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", *(*(arr + i) + j));    //也可以写成arr[i][j]
			//上面这个是挨着访问的,具体理解我写到博客里面,在这里我不好展开
		}
		printf("\n");
	}
	return 0;
}

 

  下面我们来对上面的代码进行更详细的解释 ,首先我们先要设置三个数组,里面都放着想对应的元素,是为了后续二维数组更加的完善,我们先把每个数组的首地址放到指针数组里面,为了可以把每个元素都打印出来,我们设置了两层循环,外层循环三次的原因是因为先选好每一个数组是什么,就比如i是0的时候,这时候开始访问第一个数组的地址,我们之后在进行循环就可以打印出第一个数组的内容了,这里的知识点涉及到了上面小编写过的《使用指针访问数组》就写到了如何用指针来写出数组,之后每次i++就会打印每一行数组的内容了,然后我们便模拟出了二维数组,小编画了两张抽象图片供读者理解:

 

  这个图虽然有点抽象,但我也画出了大概的雏形,就比如右图代表着设置了三个数组,左边代表着指针数组里面包含着三个数组首元素的地址。

小结:

  数组指针是指针中较为重要的知识点,读者朋友们一定要好好的领悟这个,不然后期真的很容易和数组指针弄混,小编建议大家可以看完文章后敲一下我在这篇文章写过的代码一定会有收获的!

总结:

  可算是写完这篇文章了,这篇文章可以看作是指针的进阶篇,指针从这便开始上强度了,大家一定要好好的学会指针,学会指针C语言便会打下半壁江山,加油,以后大厂的程序员们!如果文章出现了差错,欢迎从评论区指出,希望各位可以多点点赞,您的点赞便是我进步的动力,我们下一篇见啦!

 

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

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

相关文章

【数据结构】第十七弹---C语言实现选择排序

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、选择排序 1.1、基本思想 1.2、代码实现 1.3、代码测试 1.4、时空复杂度分析 总结 1、选择排序 1.1、基本思想 选择排序是一种简单直观的比…

【测试专题】系统测试报告(原件Word)

软件测试报告在软件开发过程中起着至关重要的作用&#xff0c;主要有以下几个主要原因&#xff1a; 1、确保软件质量 2、提供决策支持 3、记录测试过程和结果 4、促进沟通和协作 5、符合标准和法规要求 6、改进测试流程和策略 7、降低风险 软件开发全套资料获取进主页或者本文末…

如何判断三相交流电子负载的性能

三相交流电子负载是模拟实际负载的设备&#xff0c;用于测试电源、变频器、逆变器等电力电子设备的性能。在购买和使用三相交流电子负载时。 三相交流电子负载能够稳定输出的最大有功功率&#xff0c;额定功率越高&#xff0c;说明负载的承载能力越强。在选择三相交流电子负载时…

计算机相关专业是否仍是“万金油”的选择?

亲爱的朋友们&#xff1a; 2024 年高考已然落幕&#xff0c;数百万高三学子站在了人生的重要十字路口&#xff0c;面临着选择大学专业这一关键抉择。在这个节点上&#xff0c;计算机相关专业是否还能被称为“万金油”的选择呢&#xff1f; 相信大家都知道&#xff0c;在最近这几…

【前端项目笔记】2 主页布局

主页布局 element-ui提供的组件名称就是它的类名 ☆☆ CSS选择器&#xff1a; &#xff08;1&#xff09;基本选择器 类型选择器 p/span/div…… 类选择器 (.classname) ID选择器 (#idname) 通配选择器 ( * ) &#xff08;2&#xff09;属性选择器 选择具有特定属性或属性值的…

k8s删除状态为 Terminating 的pod

卸载calico pod时候pod资源状态会卡在terminating&#xff0c;这时候需要手动进行删除 使用以下命令即可 kubectl delete pod podName -n NAMESPACE --force --grace-period0记住一定要加命名空间&#xff0c;不然会报错没有找到

Android可穿戴设备世界之旅

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 介绍 Android通过在电视、穿戴和汽车等各种电子模块中扩展下一代应用开发概念&#xff0c;扩展了其整个范围和可…

计算机网络:6应用层

概述 客户/服务器方式和对等方式 客户/服务器&#xff08;Client/Server&#xff0c;C/S&#xff09;方式 客户和服务器是指通信中所涉及的两个应用进程。 客户/服务器方式所描述的是进程之间服务和被服务的关系。 服务器总是处于运行状态&#xff0c;并等待客户的服务请求。 …

C# + easyui 写的一个web项目

用C# easyui 来开发&#xff0c;其实就是为了开发速度&#xff0c;用easyui可以一天写很多页面&#xff0c;比一些低代码平台还快。 登陆页面 主界面 记录数统计 家庭信息采集表 新建家庭 家庭成员 低保、五保人员帮扶情况登记表 低保、五保人员帮扶情况登记表的新增和编辑 治…

【星海随笔】云解决方案学习日志篇(二) kafka、Zookeeper、Fielbeat

Elastic 中国社区官方博客 https://blog.csdn.net/ubuntutouch/category_9209092.html Kafka kafka的源代码是基于Scala语言编写的&#xff0c;运行在Java虚拟机&#xff08;即:JVM&#xff09;上。因此&#xff0c;在安装kafka之前需要先安装JDK Kafka 为什么依赖 Zookeepe…

数据库、中台、报表平台之间的关系

我最近在接触报表平台和中台&#xff0c;发现他们跟我平常用的数据库不是一个东西。然后&#xff0c;我开始了摸索他们的过程&#xff0c;终于&#xff0c;我在理清他们的关系以后&#xff0c;简单写一个入门级的区分。 数据库&#xff1a; 定义&#xff1a; 数据库是被长期存…

HarmonyOS开发日记 :自定义节点,实现 UI 组件 动态创建、更新

引言 UI动态操作包含组件的动态创建、卸载、更新等相关操作。 通过组件预创建&#xff0c;可以满足开发者在非build生命周期中进行组件创建&#xff0c;创建后的组件可以进行属性设置、布局计算等操作。之后在页面加载时进行使用&#xff0c;可以极大提升页面响应速度。 UI …

S32K3通过S32DS实现:S32K3如何将FLASH驱动放到RAM里面、RAM如何实现软件复位数据不丢失操作。

目录 1、概述 2、默认flash存放位置展示 3、通过默认的链接文件将flash放置到RAM 4、通过修改启动与链接文件将flash放在RAM 5、RAM热复位数据不丢失 1、概述 在通过RTD的SDK也好MCAL也好,始终存在一个问题,生成的代码除了看门狗模块,默认都是放在flash里面,按照正常逻…

【数据结构与算法】运算受限的线性表(栈,队列)重要知识点详解

栈和队列是什么样的线性表? 栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;都是运算受限的线性表。 栈&#xff1a;栈是一种特殊的线性表&#xff0c;只允许在一端&#xff08;通常称为“顶端”&#xff09;进行插入和删除操作。栈遵循后进先出&#x…

AI播客下载:The TWIML AI Podcast (机器学习与人工智能周刊)

机器学习和人工智能正极大地改变着企业的运营方式和人们的生活方式。TWIML AI 播客将机器学习和人工智能领域的顶尖思想和理念带给了一个广泛的、有影响力的社区&#xff0c;这个社区包括机器学习/人工智能研究人员、数据科学家、工程师以及技术娴熟的商业和 IT 领导者。主持人…

EasyRecovery下载_EasyRecovery官方下载_2024最新版软件安装包附加详细安装步骤

EasyRecovery中文版是一款操作安全、恢复性比较高的数据恢复工具&#xff0c;小伙伴们可以使用EasyRecovery恢复各种各样被删除的文件、视频、图片等。EasyRecovery还可以支持恢复从硬盘、光盘、U盘、数码相机、手机等各种设备中恢复被删除或丢失的文件&#xff0c;只是使用Eas…

【验证码识别】Yolov8实战某验3空间推理点选验证码,目标检测,语义分割,颜色分类。

【验证码识别】Yolov8实战某验3空间推理点选验证码&#xff0c;目标检测&#xff0c;语义分割&#xff0c;颜色分类。 文章目录 【验证码识别】Yolov8实战某验3空间推理点选验证码&#xff0c;目标检测&#xff0c;语义分割&#xff0c;颜色分类。声明1.空间推理验证码&#xf…

HTML的常用标签

HTML&#xff08;补&#xff09; CSS选择器 元素选择器&#xff1a;指定一个标签给这个标签设置一个默认的样式。设置的样式对所有相同的标签都有用。 id选择器&#xff1a;我们可以给标签指定一个唯一的id&#xff0c;然后根据id可以在style标签中设置对应标签的样式元素。设…

CentOS 7 安装MySQL以及常见问题解决

访问网站&#xff1a;http://repo.mysql.com 找到适配CentOS 7版本的MySQL 的YUM仓库包rpm文件&#xff0c;如下图 下载后&#xff0c;找到安装包的位置 空白处右键&#xff0c;选择在终端打开 查看当前目录下文件 # 安装MySQL 5.7的YUM仓库包rpm -ivh mysql57-community-rele…

GD32错误调试篇:串口通讯乱码/stm32移植到GD32后串口通讯乱码等问题

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 向上代码兼容GD32F450ZGT6中使用 后续项目主要在下面该专栏中发布&#xff1a; https://blog.csdn.net/qq_62316532/category_12608431.html?spm1001.2014.3001.5482 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转…