深入理解指针2

各位小伙伴们,我们继续来学习指针,指针和结构体以及动态内存管理对后面的数据结构学习有非常大的帮助,所有我们一定要把这些知识点学会。OK,正式进入学习之旅吧

1.数组名的理解

在上⼀个章节我们在使⽤指针访问数组的内容时,有这样的代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
这⾥我们使⽤ &arr[0] 的⽅式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址,而且
是数组⾸元素的地址,我们可以来做个测试。
int main()
{int arr[10] = { 0 };printf("%p\n", &arr[0]);printf("%p\n", arr);return 0;
}

我们发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样,数组名就是数组⾸元素(第⼀个元素)的地址。
但是如果数组名是首元素的地址的话,那么我们可以看一看下面这个代码输出结果是什么?
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));return 0;
}
输出的结果是:40,如果arr是数组⾸元素的地址,那输出应该的应该是4/8才对。其实数组名是首元素的地址这句话是对的,但是有2个例外:

1.sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表示整个数组,计算的是整个数组的大小,单位是字节。

2.&数组名,这⾥的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)。​​​​​​​

除此之外,任何地方使用数组名,数组名都表示首元素的地址。
我们继续试一试下面这个代码
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n", arr);printf("&arr = %p\n", &arr);return 0;
}

奇怪的是三个代码的结果一模一样,那么arr和&arr区别在哪?

我们可以看一下这段代码,就知道数组首元素的地址和整个数组的地址到底是怎么回事了

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0]   = %p\n", &arr[0]);printf("&arr[0]+1 = %p\n", &arr[0]+1);printf("arr       = %p\n", arr);printf("arr+1     = %p\n", arr+1);printf("&arr      = %p\n", &arr);printf("&arr+1    = %p\n", &arr+1);return 0;
}

这⾥我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是⾸元素的地址,+1就是跳过⼀个元素。
但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。
到这⾥大家应该搞清楚数组名的意义了吧。

2.使用指针访问数组

有了前⾯知识的支持,再结合数组的特点,我们就可以很方便的使⽤指针访问数组了。
int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;int i = 0;for (int i = 0; i < sz; i++){scanf("%d", p + i);//scanf("%d", arr+i);//也可以这样写}for (i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}

这个代码搞明白后,我们再试⼀下,如果我们再分析⼀下,数组名arr是数组⾸元素的地址,可以赋值给p,其实数组名arr和p在这⾥是等价的。那我们可以使⽤arr[i]可以访问数组的元素,那p[i]是否也可以访问数组呢?答案是可以的。 我们只需要将打印的代码*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i] 是等价于 *(p+i)。
同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移量求出元素的地址,然后解引⽤来访问的。
因为加法支持交换律,所以还能写成这样”i[arr]“,*(i+arr)等价于i[arr]。不过这种写法不常用,小伙伴们还是用自己习惯的方式来写。

3.一维数组传参的本质

数组我们学过了,之前也讲了,数组是可以传递给函数的,这个小节我们讨论⼀下数组传参的本质。⾸先从⼀个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把数组传给⼀个函数后,函数内部求数组的元素个数吗?我们可以先想一个下面这段代码的结果是什么。
void test(int arr[])
{int sz = sizeof(arr) / sizeof(arr[0]);printf("%d", sz);
}int main()
{int arr[10] = { 0 };test(arr);return 0;
}

结果是1,这是因为数组传参的时候,传递的并非是数组,而是是首元素的地址,传地址要用指针来接收。在x86环境下,int类型的指针变量大小是4个字节,一个元素的大小是4,所以结果是1。

也就是说本质上数组传参本质上传递的是数组首元素的地址。
函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的大小(单位字节)⽽不是数组的大小(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。
总之一句话:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

 4.二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥?所以接下来我们要学习二级指针。

 

对于⼆级指针的运算有:
*pp 通过对pp中的地址进⾏解引⽤,这样找到的是 p *pp  其实访问的就是 p。
**pp 先通过 *pp  找到 ,然后对 进⾏解引⽤操作: *p  ,那找到的是 a。

5.指针数组

指针数组是指针还是数组?
我们类比⼀下,整型数组:是存放整型的数组;字符数组:是存放字符的数组。
那指针数组呢?是存放指针的数组。这样写是不是比较好理解了。如图所示
那么指针数组到底有什么用呢?接下来我们通过一段代码来看一下有什么用

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

值得注意的是实际上并非完全是⼆维数组,因为每⼀行并非是连续的。

int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* arr[3] = { 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]);}printf("\n");}return 0;
}

arr[i]是访问arr数组的元素,arr[i]找到的数组元素指向了整型⼀维数组,arr[i][j]就是整型⼀维数组中的元素。
OK,今天指针内容的学习就到这里了,还有一部分指针的内容我们以后在学,小伙伴们一定要把今天的知识点吃透,我们再接再励。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免费分享一套SpringBoot+Vue实验室(预约)管理系统,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue实验室(预约)管理系统 &#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue实验室(预约)管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue实验室(预约)管理系统 Java毕…

微信小程序 wxs内联与外联的写法

内联写法 <!-- 内联wxs --> <view>大写字母{{m1.toUpper("xmly")}}</view> <wxs module"m1">module.exports.toUpperfunction(str){return str.toUpperCase()} </wxs> 外联写法 新建一个wxs文件 写一个函数&#xff0c;将…

python实现时间序列预测模型实战案例

时间序列预测是预测时间序列未来值的过程。这种预测在多种领域都有应用&#xff0c;包括金融、气象、销售预测等。下面是一个使用Python和Facebook的Prophet库进行时间序列预测的实战案例。 Prophet是一个用于时间序列预测的Python库&#xff0c;由Facebook开发并开源。它特别…

争取用一张图说清楚什么是编译器(Compiler)

抽完一颗烟&#xff0c;回来&#xff0c;突然看到了德州仪器的各个DSP芯片的编译器&#xff0c;想到了给编译器做个说明&#xff0c;争取对编译器概念有点糊涂的小朋友能突然一下明白&#xff0c; ┗|&#xff40;O′|┛ 嗷~~ 编译器就是 人机间 语言 的 翻译官&#xff08;Tra…

YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information

论文地址&#xff1a;https://arxiv.org/pdf/2402.13616.pdf 代码地址&#xff1a;GitHub - WongKinYiu/yolov9: Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information yolov7的作者更新了yolov9 摘要 当今的深度学习方…

TypeScript基础知识点

TypeScript 简介和发展 TypeScript 是一种由微软开发的开源编程语言&#xff0c;其设计目标是为了解决 JavaScript 在大规模应用开发时所面临的类型安全和可维护性问题。TypeScript 的诞生背景可以追溯到2012年&#xff0c;当时随着Web应用程序复杂度的日益增长&#xff0c;J…

PCIe 5.0 Layout Guide笔记

一、松耦合和紧耦合 松耦合优点是相同走线宽度下电介质更薄,同时对线间距的变化不敏感,提供了更好的阻抗控制;松耦合缺点是需要更大的区域进行绕线;紧耦合优点是更高的布线密度,相同阻抗下走线可以更细,同时具有更好的共模噪声抑制;紧耦合缺点是阻抗随线间距的变化大;【…

一个模板项目复制多个时候报错

复制的项目虽然用新idea打开&#xff0c;但是用的parent版本号&#xff0c;或者名字还是一个&#xff0c;这块要改&#xff0c;改版本号就行&#xff0c;子项目也改 循环依赖解决 pom中引入了循环依赖&#xff0c;pom已经最新&#xff0c;但是启动还是报错&#xff0c;idea缓…

Llama中文大模型

关于Llama中文大模型 欢迎来到Llama中文大模型&#xff1a;已经基于大规模中文数据&#xff0c;从预训练开始对Llama2模型进行中文能力的持续迭代升级。 Llama中文大模型 &#xff1a;https://github.com/LlamaFamily/Llama-Chinese 在线体验 &#xff1a;https://llama.fam…

基于Java的艺培管理解决方案

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

cmake构建在Visual stdio 2019 和Xcode的Qt的程序

概述&#xff1a;用CMake可以方便地构建Qt的应用程序&#xff0c;前提是你已经配置好用Visual Stdio 开发的Qt的环境或者Xcode的Qt开发环境。 1、编写CMakeLists.txt cmake_minimum_required(VERSION 3.6)set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STR…

你有一份关于alzet渗透泵的知识,请签收

alzet渗透泵是ALZET公司在二十世纪七十年代发明的一种用于体内药物缓释的工具。起初&#xff0c;alzet渗透泵仅仅是在公司内部和相关科研机构用来做药物测试的&#xff0c;当时alzet渗透泵并没有大量流入市场&#xff0c;但在不久之后&#xff0c;由于胶囊泵与所植入部位的组织…

介绍 Gradio 与 Hugging Face

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 我们可以使用 Gradio 库为我们的模型构建演示。Gradio 允许您完全使用 Python 为任何机器学习模型构建、自定义和共享基于 Web 的演示。使机器学习模型变得可交互和易于使用。 为什么首先要为您的机器…

OSDI 2023: Nimble Rollback Protection for Confidential Cloud Services

我们使用以下6个分类标准对本文的研究选题进行分析: 1. 研究领域 TEE回滚保护: 侧重于防止攻击者将系统恢复到早期状态,可能会撤消有效交易或引入不一致。 该领域的解决方案涉及检测和缓解回滚尝试的机制,确保TEE内的数据完整性。状态连续性: 解决如何在计算过程中保证TEE内…

Parade Series - NVR Stat

获取文件夹占用空间信息 DIR %NVRHOME% /W /SDIR %NVRHOME% /s | tail -n2 | sed s/,//g | awk {if(NR1){key"Used";}else{key"Free";};sum$3/(1024*1024);unit"MB";if(sum^>1024){sumsum/1024;unit"GB";}printf("{\"Ty…

Ansible group模块 该模块主要用于添加或删除组。

目录 创建组验证删除组验证删除一个不存在的组 常用的选项如下&#xff1a; gid  #设置组的GID号 name  #指定组的名称 state  #指定组的状态&#xff0c;默认为创建&#xff0c;设置值为absent为删除 system  #设置值为yes&#xff0c;表示创建为系统组 创建组 ansib…

【Leetcode】938. 二叉搜索树的范围和

文章目录 题目思路代码结论 题目 题目链接 给定二叉搜索树的根结点 root&#xff0c;返回值位于范围 [low, high] 之间的所有结点的值的和。 示例 1&#xff1a; 输入&#xff1a;root [10,5,15,3,7,null,18], low 7, high 15 输出&#xff1a;32 示例 2&#xff1a; 输入…

linux+fortify

系统版本:Ubuntu22.04桌面版 一:登录root用户关闭防火墙 sudo passwd root systemctl stop ufw.service systemctl disable ufw.service 二:开启ssh服务 sudo apt update sudo apt install openssh-server sudo systemctl start ssh sudo systemctl status ssh连接上WinS…

笔记本如何录屏?很简单,我来告诉你

“最近遇到了一些工作上的问题&#xff0c;需要录制一些会议和讨论的内容&#xff0c;以便于后续的整理和回顾。但是&#xff0c;我没有使用过笔记本进行录屏&#xff0c;不知道该如何操作。大家有没有简单易懂的笔记本录屏指南&#xff0c;教教我&#xff01;” 在当今数字化…

vue3(vite)+electron打包踩坑记录(1)

vue3(vite)electron打包踩坑记录 - 打包vue 第一步 编译vue 使用vite构建vue&#xff0c;package.json如下 {"name": "central-manager","private": true,"version": "0.0.0","type": "commonjs",&q…