当前位置: 首页 > news >正文

语言深入理解指针(非常详细)(三)

目录

  • 数组名的理解
    • 使用指针访问数组
  • 一维数组传参的本质
  • 二级指针
  • 指针数组
  • 指针数组模拟二维数组

数组名的理解

在上⼀个章节我们在使用指针访问数组的内容时,有这样的代码:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];

这里我们使用 &arr[0] 的方式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址,而且
是数组首元素的地址,我们来做个测试

#include <stdio.h>
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);
return 0;
}

输出结果如下:
在这里插入图片描述
我们发现数组名和数组首元素的地址打印出的结果⼀模⼀样,数组名就是数组首元素(第⼀个元素)的地
址。
但是我们再来看一个代码

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

如果是首元素的地址,那么sizeof(arr)也应该是首元素的大小,那么就是4或者是8,然而结果却是40。

其实首元素地址是对的,但是这里的sizeof(arr)是整个数组的地址(只是两个地址相同而已,但是还是有区别的就像sizeof(arr)),下面是两个例外:

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

• &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素
的地址是有区别的)

除此之外,任何地方使用数组名,数组名都表示首元素的地址。
我们再来看一段代码

#include <stdio.h>
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+1后与原来的地址相比较相差4个字节,而&arr+1后相差40个字节,刚好是整个数组元素的大小,因此我们可以推断,取整个元素地址时我们+1是移动整个数组的大小.

使用指针访问数组

#include <stdio.h>
int main()
{
int arr[10] = {0};
//输⼊
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
//输⼊
int* p = arr;
for(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]是否也可以访问数组呢

#include <stdio.h>
int main()
{
int arr[10] = {0};
//输⼊
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
//输⼊
int* p = arr;
for(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;
}

在第18行的地方,将* (p+i)换成p[i]也是能够正常打印的,所以本质上p[i]是等价于*(p+i)。
同理arr[i]应该等价于*(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移
量求出元素的地址,然后解引用来访问的
其实在计算机处理时都是变成为*(arr+i)的形式
这种其实就类似于加法的交换性质
arr[i]=(arr+i)=(i+arr)=i[arr]

一维数组传参的本质

#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;
}

在这里插入图片描述
我们发现在函数内部是没有正确获得数组的元素个数
数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组首元素的地址
所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写sizeof(arr) 计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分的本质是指针,所以在函数内部是没办法求的数组元素个数的

void test(int arr[])//参数写成数组形式,本质上还是指针
{
printf("%d\n", sizeof(arr));
}
void test(int* arr)//参数写成指针形式
{
printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
test(arr);
return 0;
}

在这里插入图片描述
在这里插入图片描述
运行结果也验证了一维数组的传参本质就是指针
总结:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式

二级指针

指针变量也是变量,是变量就有地址。
因此二级指针是可以存放一级指针的地址。(在学二级指针时我感觉有点像数学中的复合函数,函数里面又有一个函数)
我们来看看一个代码

#include<stdio.h>
int mian(){
int a=10;
int *pa=&a;
int **ppa=&pa;
return 0;
}

我们分析一下这个代码
intp中代表p是指针变量,储存的是a的地址,而对应的a类型是整形类型,因此pa前面有一个int。
而int **p中int * 是表示pa的类型是指针变量,因此要用int
来表示pa,而第二个*则表示ppa是指针变量,储存的是pa的地址
对于二级指针的运算有:
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa

int b = 20;
*ppa = &b;//等价于 pa = &b

*ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a

**ppa = 30;
//等价于*pa = 30;
//等价于a = 30

指针数组

指针数组是指针还是数组?
我们类比一下,整型数组,是存放整型的数组,字符数组是存放字符的数组。那指针数组呢?是存放指针的数组。
我们可以看出像(###)(xxx)这样的表示,###就是存放的元素,而xxx则是存放的方式
在这里插入图片描述
数组指针的每个元素都是用来存放地址(指针)的((数组)(指针)就是通过指针来存放数组)
如下图:
在这里插入图片描述
数组指针的每个元素是地址,又可以指向一块区域

指针数组模拟二维数组

#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数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}

我们来看看运行结果
在这里插入图片描述
这是二维数组的运行结果
在这里插入图片描述arr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数
组中的元素。
上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的(其实就是因为在穿件数组时,因为不是同一个数组,因此中间不知道隔了多少的字节)

在这里插入图片描述

相关文章:

语言深入理解指针(非常详细)(三)

目录 数组名的理解使用指针访问数组 一维数组传参的本质二级指针指针数组指针数组模拟二维数组 数组名的理解 在上⼀个章节我们在使用指针访问数组的内容时&#xff0c;有这样的代码&#xff1a; int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0];这里我们使用 &am…...

实训笔记8.31

实训笔记8.31 8.31笔记一、项目开发流程一共分为七个阶段1.1 数据产生阶段1.2 数据采集存储阶段1.3 数据清洗预处理阶段1.4 数据统计分析阶段1.5 数据迁移导出阶段1.6 数据可视化阶段 二、项目数据清洗预处理的实现2.1 清洗预处理规则2.1.1 数据清洗规则2.1.2 数据预处理规则 2…...

el-table 垂直表头

效果如下&#xff1a; 代码如下&#xff1a; <template><div class"vertical_head"><el-table style"width: 100%" :data"getTblData" :show-header"false"><el-table-columnv-for"(item, index) in getHe…...

B081-Lucene+ElasticSearch

目录 认识全文检索概念lucene原理全文检索的特点常见的全文检索方案 Lucene创建索引导包分析图代码 搜索索引分析图代码 ElasticSearch认识ElasticSearchES与Kibana的安装及使用说明ES相关概念理解和简单增删改查ES查询DSL查询DSL过滤 分词器IK分词器安装测试分词器 文档映射(字…...

机器学习:塑造未来的核心力量

着科技的飞速发展&#xff0c;机器学习已经成为我们生活中不可或缺的一部分。无论是搜索引擎、推荐系统&#xff0c;还是自动驾驶汽车和机器人&#xff0c;都依赖于机器学习算法。本文将探讨机器学习的基本概念、应用领域以及未来发展趋势。 一、机器学习的基本概念 机器学习…...

RK3568-i2c-适配8010rtc时钟芯片

硬件连接 从硬件原理图中可以看出&#xff0c;rtc时钟芯片挂载在i2c3总线上&#xff0c;设备地址需要查看芯片数据手册。编写设备树 &i2c3 {status "okay";rx8010: rx801032 {compatible "epson,rx8010";reg <0x32>;}; };使能驱动 /kernel/…...

Spring Security - 基于内存快速demo

基于内存方式 - 只作学习参考1.引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>2.login.html、index.html、fail.htmllogin.html:<form method…...

6 | 从文本文件中读取单词并输出不重复的单词列表

Transformation 操作 Transformation 操作是用于从一个 RDD(Resilient Distributed Dataset)创建一个新的 RDD,通常是通过对原始 RDD 的元素进行映射、筛选、分组等操作来实现的。Transformation 操作不会立即执行,而是惰性计算,只有在 Action 操作触发时才会真正执行。以…...

【微信小程序篇】- 多环境(版本)配置

最近自己在尝试使用AIGC写一个小程序&#xff0c;页面、样式、包括交互函数AIGC都能够帮我完成(不过这里有一点问题AIGC的上下文关联性还是有限制&#xff0c;会经常出现对于需求理解跑偏情况&#xff0c;需要不断的重复强调&#xff0c;并纠正错误&#xff0c;才能得到你想要的…...

ssh配置(一、GitLabGitHub)

一. 为什么配置ssh 使用 ssh 克隆项目&#xff0c;更加安全方便。 git clone 项目时一般使用两种协议 https 和 ssh 。 二. 原理的通俗解释 ssh 解决的问题是登录时的用户身份验证问题&#xff0c;默认使用 RSA&#xff08;也支持其他算法&#xff1a; RSA、DSA、ECDSA、EdD…...

开了抖店后就可以直播带货了吗?想在抖音带货的,建议认真看完!

我是王路飞。 关于抖店和直播带货的关系&#xff0c;其实很多人经常搞不清楚。 不然的话&#xff0c;也不会有这个问题的出现了&#xff1a;开了抖店后就可以直播带货了吗&#xff1f; 在我看来&#xff0c;这个问题很简单&#xff0c;但在不了解抖音电商和直播带货其中门道…...

【深度学习实验】数据可视化

目录 一、实验介绍 二、实验环境 三、实验内容 0. 导入库 1. 归一化处理 归一化 实验内容 2. 绘制归一化数据折线图 报错 解决 3. 计算移动平均值SMA 移动平均值 实验内容 4. 绘制移动平均值折线图 5 .同时绘制两图 6. array转换为tensor张量 7. 打印张量 一、…...

【Golang】函数篇

1、golang函数基本定义与使用 func 函数名 (形参列表) (返回值类型列表) {函数体return 返回值列表 }其中func用于表明这是一个函数&#xff0c;剩下的东西与其他语言的函数基本一致&#xff0c;在定义与使用的时候注意函数名、参数、返回值书写的位置即可。下面使用一个例子…...

在ubuntu上安装ns2和nam(ubuntu16.04)

在ubuntu上安装ns2和nam 版本选择安装ns2安装nam 版本选择 首先&#xff0c;版本的合理选择可以让我们避免很多麻烦 经过测试&#xff0c;ubuntu的版本选择为ubuntu16.04&#xff0c;ns2的版本选择为ns-2.35&#xff0c;nam包含于ns2 资源链接(百度网盘) 链接:https://pan.bai…...

SpringCloudAlibaba之Sentinel介绍

文章目录 1 Sentinel1.1 Sentinel简介1.2 核心概念1.2.1 资源1.2.2 规则 1.3 入门Demo1.3.1 引入依赖1.3.2 集成Spring1.3.3 Spring中资源规则 1.4 Sentinel控制台1.5 核心原理1.5.1 NodeSelectorSlot1.5.2 ClusterBuilderSlot1.5.3 LogSlot1.5.4 StatisticSlot1.5.5 Authority…...

苹果微信聊天记录删除了怎么恢复?果粉原来是这样恢复的

粗心大意删除了微信聊天记录&#xff1f;有时候&#xff0c;一些小伙伴可能只是想要删除一部分聊天记录&#xff0c;但是在进行批量删除时&#xff0c;不小心勾选到了很重要的对话&#xff0c;从而导致记录丢失。 如果这时想找回聊天记录该怎么办&#xff1f;微信聊天记录删除…...

JVM的故事——虚拟机字节码执行引擎

虚拟机字节码执行引擎 文章目录 虚拟机字节码执行引擎一、概述二、运行时栈帧结构三、方法调用 一、概述 执行引擎Java虚拟机的核心组成之一&#xff0c;它是由软件自行实现的&#xff0c;能够执行那些不被硬件直接支持的指令集格式。 对于不同的虚拟机实现&#xff0c;执行引…...

设计模式之适配器与装饰器

目录 适配器模式 简介 角色 使用 优缺点 使用场景 装饰器模式 简介 优缺点 模式结构 使用 使用场景 适配器模式 简介 允许将不兼容的对象包装成一个适配器类&#xff0c;使得其他类可以通过适配器类与原始对象进行交互&#xff0c;从而提高兼容性 角色 目标角色…...

服务器数据恢复- Ext4文件系统分区挂载报错的数据恢复案例

Ext4文件系统相关概念&#xff1a; 块组&#xff1a;Ext4文件系统的空间被划分为若干个块组&#xff0c;每个块组内的结构大致相同。 块组描述符表&#xff1a;每个块组都对应一个块组描述符&#xff0c;这些块组描述符统一放在文件系统的前部&#xff0c;称为块组描述符表。每…...

19-springcloud(上)

一 微服务架构进化论 单体应用阶段 (夫妻摊位) 在互联网发展的初期&#xff0c;用户数量少&#xff0c;一般网站的流量也很少&#xff0c;但硬件成本较高。因此&#xff0c;一般的企业会将所有的功能都集成在一起开发一个单体应用&#xff0c;然后将该单体应用部署到一台服务器…...

前端基础---HTML笔记汇总一

HTML定义 HTML超文本标记语言——HyperText Markup Language。 超文本是什么&#xff1f; 链接标记是什么&#xff1f; 标记也叫标签&#xff0c;带尖括号的文本 标签分类 单标签:只有开始标签&#xff0c;没有结束标签(<br>换行 <hr>水平线 <img> 图像标…...

智汇云舟亮相中国安防工程商集成商大会

智汇云舟亮相中国安防工程商集成商大会&#xff0c;以视频孪生驱动安防行业数字化转型 近日&#xff0c;由中国安全防范产品行业协会指导&#xff0c;永泰传媒主办的中国安防工程商&#xff08;系统集成商&#xff09;大会暨第69届中国安防新产品、新技术成果展示在石家庄圆满…...

使用 Sealos 在离线环境中光速安装 K8s 集群

作者&#xff1a;尹珉。Sealos 开源社区 Ambassador&#xff0c;云原生爱好者。 当容器化交付遇上离线环境 在当今快节奏的软件交付环境中&#xff0c;容器化交付已经成为许多企业选择的首选技术手段。在可以访问公网的环境下&#xff0c;容器化交付不仅能够提高软件开发和交付…...

算法-模拟

1、旋转数组 public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** 旋转数组* param n int整型 数组长度* param m int整型 右移距离* param a int整型一维数组 给定数组* return int整型一维数组*/…...

如何通过Instagram群发消息高效拓展客户?

之前小S有跟大家说过关于独立站&#xff0b;Instagram如何高效引流&#xff0c;发现大家都对Instagram的话题挺关注的。Instagram作为全球最受欢迎的社交媒体之一&#xff0c;对于许多商家和营销人员来说&#xff0c;Instagram是一个不可忽视的营销平台&#xff0c;他们可以通过…...

基于springboot实现多线程抢锁的demo

1、本代码基于定时调度和异步执行同时处理&#xff0c;如果只加异步处理&#xff0c;会导致当前任务未执行完&#xff0c;下个任务到点也不会触发执行 Scheduled(fixedRate 50_000)Asyncpublic void testThread() throws Exception{ZkLock lock new ZkLock(zkJob.getZK(), &q…...

Java I/O模型发展以及Netty网络模型的设计思想

Java I/O模型发展以及Netty网络模型的设计思想 I/O模型Java BIOJava NIOJava AIO NIO Reactor网络模型单Reactor单线程模型单Reactor多线程模型主从Reactor多线程模型 Netty通信框架 前言&#xff1a; BIO、NIO的代码实践参考&#xff1a;Java分别用BIO、NIO实现简单的客户端服…...

智能电网时代:数字孪生的崭露头角

随着科技的不断进步&#xff0c;数字孪生已经开始在电力行业崭露头角&#xff0c;为这个关键的行业带来了前所未有的机遇和潜力。本文就带大家了解一下数字孪生在哪些方面为电力行业做出改变&#xff0c;以及未来的创新应用。 首先&#xff0c;数字孪生可以提高电力系统运营效率…...

每日一题 501二叉搜素树中的众数(中序遍历)

题目 给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 如果树中有不止一个众数&#xff0c;可以按 任意顺序 返回。 假定 BST 满足如下定义&a…...

测试理论与方法----测试流程第三个环节:设计测试用例

测试流程第三个环节&#xff1a;设计测试用例&#xff1a;怎么测<——>测试需求的提取&#xff1a;测什么 ### 5、测试用例 描述&#xff1a;测试用例(TestCase)&#xff1a;是一份关于【具体测试步骤】的文档&#xff0c;是为了达到最佳的测试效果或高效揭露软件中潜藏的…...