指针的深入理解(3)(包括数组名的理解、一维数组传参的本质以及指针数组的相关知识及使用)


文章目录

  • 1 数组名的理解
  • 2 使用指针访问数组
  • 3 一维数组传参的本质
  • 4 指针数组
  • 5 指针数组的使用


1 数组名的理解

当我们运行以下代码:

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

在这里插入图片描述
会发现,arr[0]的地址和数组名arr的地址是一样的。

所以我们大致可以猜测一下:
数组名是数组首元素的地址

但是按照这样的理解,以下代码的结果应该是4才对

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

但是当我们运行就会发现,结果是40.
在这里插入图片描述
那说明之前的猜测有一些问题。

实际上的结论是:
数组名是数组首元素的地址,
但是有两个例外:
1.sizeof(数组名),这里的数组名表示整个数组,sizeof计算的是整个数组的大小,单位是字节。
2.&数组名,这里的数组名也表示整个数组,取出的是整个数组的地址

当我们运行代码,肯定会疑惑,根据结论的第二个例外,&数组名取出整个数组的地址,为什么得到的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;
}

在这里插入图片描述
其实也不难理解,取出首元素的地址其实就相当于取出了整个元素的地址,如果我们得到了首元素的地址,就可以顺藤摸瓜得到剩下所有元素的地址。

我们可以通过以下的代码观察它们之间的区别;

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

在这里插入图片描述
可以发现,三种方式得到的地址都是一样的,但是由于arr和&arr[0]取出的都是首元素的地址,把它们地址加1,加的是一个整形元素,4个字节,但是如果是**&arr,加1后地址加了40个字节**(70到98,加了28,这是16进制表示,转换成十进制就是40),所以这个例子能很好的证明&arr其实取出的是整个数组的地址,但是由于我们可以通过首地址找到所以元素的地址,为了方便显示,我们观察到的地址还是首元素的地址。

2 使用指针访问数组

如果我们想要实现数组的输入和输出时,可以这样写代码:

#include <stdio.h>
int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;int* p = arr;//向数组输入数for (i = 0; i < sz; i++){scanf("%d", p + i);	
//p最开始指向数组首元素的地址,每次输入完后要执行后面的地址进行下一次的输入}//输出数组的内容for (i = 0; i < sz; i++){printf("%d", *(p + i));}return 0;
}

在这里插入图片描述
程序能够很好的运行,但是如果我们稍作修改呢?
假如我们把scanf函数中的p+i修改成p++,结果又会是什么呢?

代码如下:

#include <stdio.h>
int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;int* p = arr;//向数组输入数for (i = 0; i < sz; i++){scanf("%d", p++);}//输出数组的内容for (i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}

在这里插入图片描述

运行后我们会发现,程序出现了问题,输出随机值,这是为什么呢?

仔细想想,最开始i=0的时候,指针指向数组首元素的地址,之后每输入一个值指针指向后一个元素的地址,但是当i=9,输入完第10个数后,p++,此时p++指向的是数组最后一个元素的后面的地址,当我们用printf输出数组时,我们自以为指针最开始还是指向数组首元素的地址,但是实际上指针指向的位置已经没有元素了,所以导致没有输出结果,如果我们想输出,就需要在输出数组之前把指针重新指向数组的首元素地址。

我们也可以通过调试来观察问题:
在调试时我们可以设置条件断点,避免无意义的重复输入,从i=8开始执行,按fn+f5开始调试后直接输入10个数组元素,然后打开监视,之后按fn+f10一直往下执行,直到执行到输入最后一个数,也就是i=9的时候,观察p所指向的地址
在这里插入图片描述
在这里插入图片描述
可以发现,当i等于9,并且输入完第10个数之后,p指向了arr[9]的后面一个元素(arr[10])的地址,但是我们创建的数组为arr[0]~arr[9],p最后指向越界了,所以在输出的时候会出现随机值。

我们可以对代码稍微进行一下修改,在打印数组内容之前使p重新指向数组的第一个元素地址,就加一句p=arr;
修改后结果如图:
修改代码如下:

3 一维数组传参的本质

当我们运行下面代码:分别在主函数和函数中求数组的大小

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

在这里插入图片描述
可以发现在函数中,数组的大小竟然是1,这是为什么呢?

我们可以尝试求出函数中arr和arr[0]的大小分别是多少
在这里插入图片描述
可以发现,传递的数组arr大小竟然也是4,所以之前求sz1的大小得到的是4/4=1.

这是因为数组传参的本质传递的是数组首元素的地址。

当我们想得到数组的大小并在函数中使用时,最好先在主函数中先计算结果,再通过参数的形式传递给函数。
在这里插入图片描述

4 指针数组

我们可以进行类比:
整形数组:存放整形的数组,例如int arr[5] = { 1, 2, 3, 4, 5}; //数组里的每一个元素都是int类型
字符数组:存放字符的数组,例如char ch[5] = { ‘a’, ‘b’, ‘c’, ‘d’, ‘e’}; //数组每个元素都是char类型
因此我们可以推断:
指针数组:存放指针的数组,数组里的每一个元素都是存放指针(地址)的
例如假设我们已知a,b,c,d,e里面存放的都是整数。
指针数组里面的元素依次存放a,b,c,d,e的地址,我们可以这样表示:int * arr[5] = { &a, &b, &c, &d, &e};
如图所示:
在这里插入图片描述

5 指针数组的使用

我们可以使用指针来模拟二维数组。
代码如下:

#include <stdio.h>
int main()
{int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 2,3,4,5,6 };int arr3[5] = { 3,4,5,6,7 };int* ptr[3] = { arr1,arr2,arr3 };int i;for (i = 0; i < 3; i++){int j;for (j = 0; j < 5; j++){printf("%d ", ptr[i][j]);}printf("\n");}return 0;
}

首先我们创建指针数组ptr,用来存放三个数组的数组名(本质存放的是数组首元素的地址),再通过双重循环找到对应的值,外层循环用来实现我们找的是哪一个数组的首元素的地址,内存循环我们可以顺着数组首元素的地址找到数组后面的所以元素并打印出来。

指向关系如图:
在这里插入图片描述

在这里插入图片描述
可以发现,三个一维数组通过指针数组的方法,模拟出了二维数组的样子,但是不等同二维数组,因为我们知道二维数组的n行在内存中是连续存放的,但是我们创建的3个一维数组并不一定在同一块空间内,我们可以得到三个数组首元素的地址,通过地址分别找到三块空间再顺藤摸瓜找到数组所有元素并打印出来。

我们知道ptr[i]在运行时会被编译器转换成指针的形式*(ptr+i),ptr[i][j]也类似,我们可以把ptr[i]当做ptr,把[j]当做[i],替换之前的指针形式就能得到ptr[i][j] = * (ptr[i]+j) = *( *(ptr+i)+j) ,所以我们在打印的时候也可以使用指针的形式,也能得到正确结果.
代码如下:

#include <stdio.h>
int main()
{int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 2,3,4,5,6 };int arr3[5] = { 3,4,5,6,7 };int* ptr[3] = { arr1,arr2,arr3 };int i;for (i = 0; i < 3; i++){int j;for (j = 0; j < 5; j++){printf("%d ", *(*(ptr+i)+j));}printf("\n");}return 0;
}

在这里插入图片描述

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

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

相关文章

HCIA6以太网基础基于MAC划分VLAN

&#xff08;简写的命令可以敲Tab按键补全剩余&#xff09; 1.组网需求 场景&#xff1a;公司的网络中&#xff0c;管理者将同一部门的员工划分到VLAN10。要求只有本部门员工的PC接入才能互访&#xff0c;其他PC接入交换机属于其他VLAN&#xff08;666&#xff09;。可以配置…

遇到Windows无法启动时不要担心,这里有解决办法

序言 如果有一天你打开电脑,Windows拒绝启动,你该怎么办?其实“Windows无法启动”是一种常见症状,原因多种多样,因此你需要进行一些故障排除。 现代版本的Windows更善于从这种情况中自动恢复,而Windows XP遇到此问题时可能会停止在运行的地方,现代版本的Windows将尝试…

【复旦邱锡鹏教授《神经网络与深度学习公开课》笔记】神经元和人工神经网络

神经元 生物神经元&#xff1a; 平时处于抑制状态&#xff0c;当接受信息量达到一定程度后进入兴奋状态。 人工神经元&#xff1a; 一个人工神经元大致有两个步骤&#xff1a; 一是收集信息&#xff0c;如上图中 x 1 , ⋯ , x d x_1,\cdots,x_d x1​,⋯,xd​表示神经元可…

SinoDB导入导出工具汇总

在进行数据迁移、数据库表备份、表重建以及批量数据加载时&#xff0c;我们经常希望数据处理过程能够更快点。本文是SinoDB导入导出工具的汇总&#xff0c;大家可以根据不同场景选择合适的SinoDB导入导出工具。 1. 各工具特点 通常利用dbschema工具导出数据库结构&#xff0c;…

NVMe中的Copy命令你知道吗?

前段时间做过copy的相关工作&#xff0c;今天抽出时间来总结一下&#xff0c;共勉 什么是Copy命令 顾名思义&#xff0c;简单理解就是复制&#xff0c;我们可以看看官方文档是如何定义的&#xff1a; The Copy command is used by the host to copy data from one or more so…

Shell脚本 if语句

条件测试&#xff1a; $? 返回码 判断命令或者脚本是否执行成功&#xff08;最近的一条&#xff09; 0 true 为真就是成功 成立 非0 false 失败或者异常 test命令 可以进行条件测试 然后根据的是返回值来判断条件是否成立。 -e 测试目录或者文件是否存在 exist -d 测试…

如何在Excel中快速找出含有多位小数的数字

在日常工作中&#xff0c;使用Excel处理数据是一项常见任务。然而&#xff0c;有时我们会遇到一些看似简单&#xff0c;却令人头疼的问题。例如&#xff0c;当我们在一个包含大量数据的列中发现某个数字的小数点位数过多时&#xff0c;如何快速找到这个数字&#xff1f;本文将介…

二开版视频CMS完整运营源码/新版漂亮APP手机模板/集成员分销功能等

一个二开的影视CMS&#xff0c;直接上传源码至网站根目录&#xff0c;访问网站域名即可安装。 测试环境&#xff1a;Nginx 1.20.1—MySQL 5.6.50–PHP-7.2&#xff08;安装拓展/fileinfo&#xff09; 上传源码&#xff0c;访问域名直接安装 后台地址&#xff1a;域名/MDadmi…

Vue + Asp.NET调试时出现的证书问题 (OpenSSL)

Vue Asp.NET调试时出现的证书问题 1. 证书过期问题步骤一:创建新的私钥步骤 2: 创建新的证书签名请求&#xff08;CSR&#xff09;步骤 3: 使用 CSR 和 CA 私钥签署新证书步骤 4: 替换或使用新证书 2. 证书不受信任问题步骤: 3. 安全证书不指定使用者可选名称步骤一: 删除已生…

基于Python + Flask+ Mysq实现简易留言板

使用Python Flask Mysql实现简易留言板&#xff0c;包括网友编辑留言、修改留言&#xff0c;删除留言、分页显示四大功能。 写出留言板建设过程&#xff0c;包括开发使用工具、留言板模块设计、数据库设计、页面设计、关键技术。 留言板建设过程总结 一&#xff0e;开发使用…

群体优化算法----狗群优化算法(注意没写错并不是狼群优化算法是狗群)介绍以及多峰函数最优解求解

介绍 狗群优化算法&#xff08;Dog Group Optimization, DGO&#xff09;是一种新兴的群体智能优化算法&#xff0c;其灵感来自于狗群的社会行为和协作方式。DGO算法利用了狗群在搜寻、合作、信息共享等方面的行为特征&#xff0c;以求解复杂的优化问题 主要概念 狗群行为&a…

云手机游戏托管的实现机制

云手机游戏托管的实现首先依赖于强大的云计算基础设施。 数据中心承载着海量的计算资源&#xff0c;通过虚拟化技术构建出一个个独立的云手机环境&#xff0c;为二游的运行提供了坚实的支撑。这些云手机具备与实体手机相当的性能&#xff0c;能够流畅地运行各类二次元游戏。 在…

仪表板展示|DataEase看中国:2024年高考数据前瞻

背景介绍 2024年高考即将来临。根据教育部公布的数据&#xff0c;2024年全国高考报名人数为1342万人&#xff0c;相比2023年增加了51万人。高考报名人数的增加&#xff0c;既体现了我国基础教育的普及范围之广&#xff0c;也反映了社会对高等教育的重视和需求。 随着中央和各…

“JS加密在线”:简单直接的在线JS加密网站

网站名&#xff1a;“JS加密在线”&#xff0c; 功能&#xff1a;JavaScript源代码加密。 UI&#xff1a; http://jsjiami.online/ 非常简洁的JS加密网站&#xff0c;几乎只有两个功能&#xff1a;上传JS文件、下载加密后的JS文件。 JS加密&#xff0c;就应该这样简单直接。…

使用随机数字或计数器在运行时计算百分比

如果我们需要在运行时计算某些项目的百分比&#xff0c;可以使用 Python 中的随机数生成器或者计数器来模拟这个过程。这取决于我们想要模拟的具体情况和场景。今天我将通过文字方式详细记录我实操过程。 1、问题背景 在处理大量交易时&#xff0c;我们需要对一定比例的交易进…

从源码分析 vllm + Ray 的分布式推理流程

一、前言 随着 LLM 模型越来越大&#xff0c;单 GPU 已经无法加载一个模型。以 Qwen-14B-Chat 模型为例&#xff0c;模型权重大概 28GB&#xff0c;但是单个 NVIDIA A10 仅有 24GB 显存。如果想要在 A10 上部署 Qwen-14B-Chat 模型&#xff0c;我们需要将模型切分后部署到 2 个…

Golang免杀-分离式加载器(传参)AES加密

目录 enc.go 生成: dec.go --执行dec.go...--上线 cs生成个c语言的shellcode. enc.go go run .\enc.go shellcode 生成: --key为公钥. --code为AES加密后的数据, ----此脚本每次运行key和code都会变化. package mainimport ("bytes""crypto/aes"&…

【Three.js】知识梳理十五:相机控制器Controls

在 3D 场景中&#xff0c;摄像机的控制尤为重要&#xff0c;因为它决定了用户如何观察和与场景互动。Three.js 提供了多种相机控制器&#xff0c;最常用的有 OrbitControls、TrackballControls、FlyControls 和 FirstPersonControls。OrbitControls 适合用于查看和检查 3D 模型…

LabVIEW进行负载测试

本文介绍了如何使用LabVIEW进行负载测试&#xff0c;通过一个具体案例详细讲解了测试系统的组成、工作原理和实现方法。系统采用先进的硬件和软件架构&#xff0c;结合LabVIEW的强大功能&#xff0c;成功实现了对设备的高效负载测试&#xff0c;确保了系统的可靠性和性能。 项…

车载网络安全指南 概述(一)

返回总目录->返回总目录<- 目录 前言 参考文档 术语 前言 汽车电子系统网络安全指南给出汽车电子系统网络安全活动框架,以及在此框架下的汽车电子系统网络安全活动、组织管理和支持保障等方面的建议。 汽车电子系统网络安全指南适用于指导整车厂、零部件供应商、软…