C语言的灵魂——指针(3)
前言:上期我们介绍了const修饰指针,saaert断言都是针对指针本身的,文章后面我们用指针与数组建立了联系,这种联系或者是关系就是这篇文章所要介绍的。上一篇文章的传送门:指针2
指针3
- 一,数组名的含义及理解
- 二,使用指针访问数组
- 三,一维数组传参的本质
- 四,二级指针
- 五,指针数组
- 六,指针数组模拟二维数组
一,数组名的含义及理解
谈到数组想必大家都知道一个点就是:数组名就是数组的首地址,也是首元素的地址。这就是数组名的含义我=我们不妨写一个代码来验证一下:
#include<stdio.h>
int main()
{int arr[10]={1,2,3,4,5,6,7,8,9,10};printf("%p\n",arr);//%p打印地址的格式占位符printf("%p\n",&arr[0]);//打印首元素的地址return 0;
}

通过运行结果我们能很明显的看到数组名就是数组首元素的地址。 既然已经知道它的含义了那我们为什么还要重点来讲它呢?如果没有例外那就不会在这里讲它了,下面我们来看代码:
#include<stdio.h>
int main()
{int arr[10]={1,2,3,4,5,6,7,8,9,10};printf("%zd\n",sizeof(arr));return 0;
}
这段代码的运行结果会是什么呢?如果我们认为arr是首元素的地址,而数组元素的类型是int类型那么打印数来的大小应该就是4个字节或8个字节才对(4个字节还是8个字节由平台大小决定)。下面我们来看结果:

结果明显与我们所想的不符,那为什么会出现这种情况呢?其实数组名就是数组首元素(第⼀个元素)的地址是对的,但是有两个例外:
1.sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小, 单位是字节。
2.&数组名,这⾥的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的。
除此之外,任何地方使用数组名,数组名都表示首元素的地址.
那我们再来看一段代码:
#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); printf("&arr = %p\n", &arr); return 0;
}

看到这里有些人可能又会懵了,前面不是说 **&数组名** 取出的是整个数组的地址吗?那为什么这里 &arr 与 arr 及 &arr[0] 打印出来的地址是一样的呢?难道整个数组的地址与首地址是一样的吗?
这里就要引用我在指针(1)指针变量和地址中介绍&这个操作符所说的了:
但有一点需要特别注意取地址取出来的是较小的地址,因为我们知道数据类型占几个字节又知道较小的地址剩下的顺藤摸瓜直接往后推算即可。

要想探究 &arr 我们只需要加上偏移量就可以很明显的看出来了:
#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("\n");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+116进制从A8变成了D0,我们用D0减去A8看看是不是40个字节就能验证&arr是不是整个数组的地址了。

我们发现&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。
看到这里大家应该搞清楚数组名的意义了吧。
二,使用指针访问数组
弄个清楚了数组名,这就让我们有了使用指针来访问数组的基础。我们来看一个常规访问数组的代码:
#include<stdio.h>
int main()
{int arr[10]={0};int i=0;int sz=sizeof(arr)/sizeof(arr[0]);for(i=0;i<sz;i++){scanf("%d",&arr[i]);}int j=0;for(j=0;j<sz;j++){printf("%d\n",arr[j]);}return 0;
}

这是我们最原始的访问数组的方法,不知大家有没有注意到一个点,其实我们以前求数组元素长度用的就是 sizeof(arr)/sizeof(arr[0]) 只是当时我们不知道。这也说明了上面说所的点sizeof(数组名)求的是整个数组的大小。
言归正传,既然数组名就是地址,而地址就是指针那么能不能使用一个指针变量指向数组,然后使用指针来访问数组呢?当然可以:
#include<stdio.h>
int main()
{int arr[10] = {0};int* p = arr;int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0;i < sz;i++){scanf("%d", p+i);//替换scanf("%d",&arr[i]);}int j = 0;for (j = 0;j < sz;j++){printf("%d ", *(p + j));//替换printf("%d\n",arr[j]);} return 0;
}
这个代码搞明⽩后,我们再试⼀下,如果我们再分析⼀下,数组名arr是数组⾸元素的地址,可以赋值 给p,其实数组名arr和p在这⾥是等价的。那我们可以使⽤arr[i]可以访问数组的元素,那p[i]是否也可 以访问数组呢?
#include<stdio.h>
void test(int arr[], int sz1)//参数本质上就是int *arr
{ //要想求数组元素个数需要传sz1过来int sz2 = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sz2);printf("%d\n", sz1);
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sz1);test(arr,sz1);return 0;
}
这样也是可行的,将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i] 是等价于 *(p+i),实际上我们写出数组下标的形式来访问数组元素的时候,编译器在处理的时候还是转换成指针来处理。
那我们想象一下 *(i+arr) 是否能写成 i[arr] 呢?答案是可以的只不过代码的可读性不高,其实 i[arr] 也不难理解[ ]这个操作符是下标引用操作符,i和arr只是它的两个两个操作数而已并不影响实际的结果。
同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移 量求出元素的地址,然后解引用来访问的。
接着我们来看看一维数组传参
三,一维数组传参的本质
前面在讲函数传参的时候我们也讲过数组传参,学完了指针后我们便可以来探索一下它的本质,先来看一段代码:
#include<stdiio.h>
//一维数组传参的时候形参是可以写成数组的形式,但是即使写成数组的形式本质其实是指针变量。
void test(int arr[],int sz1)//参数本质上就是int *arr
{ //要想求数组元素个数需要传sz1过来int sz2=sizeof(arr)/sizeof(arr[0]);printf("%d",sz2);printf("%d",sz1);
}
int main()
{int arr[10]={1,2,3,4,5,6,7,8,9,10};int sz1=sizeof(arr)/sizeof(arr[0]);printf("%d",sz1);test(arr,sz1);return 0;
}

通过运行结果我们有个疑惑为什么sz2的结果是2呢?首先我们能够确定的一点是 sizeof(arr[0]) 的大小就是4个字节(一个整型类型)这点是毫无疑问的,那么谁/4等于2呢?小学生都会,答案是8。那为什么是8呢?
注意sz2的值也可能是1这而取决于平台大小!
还记得最开始举的第二个例子吗?
如果我们认为arr是首元素的地址,地址就是指针;而数组元素的类型是int类型那么打印数来的大小应该就是4个字节或8个字节才对(4个字节还是8个字节由平台大小决定)。
所以我们得出一个非常重要的结论,数组传参本质上传过去的就数组元素的首地址(是一个指针)!
所以函数形参的部分理论上应该使用指针变量来接收⾸元素的地址。那么在函数内部我们写 sizeof(arr) 计算的是⼀个地址的大小(单位字节)⽽不是数组的⼤⼩(单位字节)。正是因为函 数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。
四,二级指针
前面我们介绍了一级指针变量,指针变量就是用来存放普通变量的地址;那以此类推指针变量的地址当然就是用二级指针来存储。
二级指针与一级指针一样无非就是对指针变量本身的使用以及解引用:
#include<stdio.h>
int main()
{
//使用指针对象int a = 10;int* pa = &a;int** ppa = &pa;
//解引用*pa = 20;printf("*pa=%d\n", *pa);int m = 100;*ppa = &m;//pa=&mprintf("%p\n", &m);printf("%p\n", pa);printf("%d\n", **ppa);//对Pa解//printf("*pa=%d\n,*pa");** ppa = 30;//直接对m进行操作printf("**ppa=%d\n",**ppa);
}


所以 *pa pa解引用就是改变a的值,改变 *ppa 对就是改变 *pa 的所存的地址,**ppa 对解引用就是改变a的值。就像有三个抽屉第一第二个抽屉都有锁,第三个抽屉放着第二个抽屉的钥匙,第二个抽屉放着第一个抽屉的钥匙。

五,指针数组
前面我们学过
字符数组——是数组,是用来存放字符的数组;char arr[5]
整型数组——是数组,是用来存放整型的数组;int arr[5]
以此类推:
指针数组——是数组,是用来存放指针的数组;int*arr[5] (存放整型指针的数组)
#include<stdio.h>
int main()
{int a=10;int b=20;int c=30;int d=40;int e=50;//与其一个个用指针变量来存放abcde的地址还不如直接用一个指针数组来存放int *arr[5]={&a,&b,&c,&d,&e};return 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数组中 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]); //parr[i]其实就是代表arr1/arr2/arr3//parr[][j]其实就是输出每一个数组内的所有元素}printf("\n"); }return 0;
}

parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数 组中的元素。 上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每一行并非是连续的。
好了以上就是本章的全部内容啦!
感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!

相关文章:
C语言的灵魂——指针(3)
前言:上期我们介绍了const修饰指针,saaert断言都是针对指针本身的,文章后面我们用指针与数组建立了联系,这种联系或者是关系就是这篇文章所要介绍的。上一篇文章的传送门:指针2 指针3 一,数组名的含义及理解…...
机器学习之Transformer 模型
Transformer 模型详解 Transformer 是由 Vaswani et al. 在 2017 年 提出的模型,最初用于 机器翻译 任务,并迅速成为自然语言处理(NLP)领域的标准模型架构。与传统的 RNN(循环神经网络) 和 LSTM(长短期记忆网络) 不同,Transformer 的核心思想是 完全基于自注意力机制…...
SSD1306 128*32屏幕驱动
最近在做一个小项目,使用合宙air001做主控,arduino开发环境,项目设计一个小屏作为显示,本身使用u8g2库,奈何这个air001空间太小,没写多少就把程序储存空间占满了,log也没办法打印,对…...
【GitHub】GitHub 2FA 双因素认证 ( 使用 Microsoft Authenticator 应用进行二次验证 )
文章目录 一、GitHub 的 2FA 双因素认证二、使用 Microsoft Authenticator 应用进行二次验证1、TOTP 应用2、下载 Microsoft Authenticator 应用3、安装使用 Authenticator 应用 三、恢复码重要性 一、GitHub 的 2FA 双因素认证 现在登录 GitHub 需要进行二次身份验证 ; 先登录…...
CNN-day5-经典神经网络LeNets5
经典神经网络-LeNets5 1998年Yann LeCun等提出的第一个用于手写数字识别问题并产生实际商业(邮政行业)价值的卷积神经网络 参考:论文笔记:Gradient-Based Learning Applied to Document Recognition-CSDN博客 1 网络模型结构 …...
AI绘画社区:解锁艺术共创的无限可能(9/10)
AI 绘画:不只是技术,更是社交新潮流 在科技飞速发展的今天,AI 绘画早已不再仅仅是一项孤立的技术,它正以惊人的速度融入我们的社交生活,成为艺术爱好者们交流互动的全新方式,构建起一个充满活力与创意的社…...
一种基于Leaflet.Legend的图例动态更新方法
目录 前言 一、场景再现 1、需求描述 2、核心方法介绍 3、存在的问题 二、问题解决 1、重复解决办法 2、图例不展示解决办法 3、成果展示 三、总结 前言 在当今数字化时代,地理信息系统(GIS)技术已经广泛应用于各个领域,…...
Three.js实现一个动态的 3D 点阵波浪效果
使用 Three.js 创建了一个动态的 3D 点阵波浪效果,可用作页面背景。它通过粒子系统生成点阵,并根据鼠标移动和时间动态调整点的位置和大小,形成波浪效果。用户可以通过组件的 props 控制波浪的长度、颜色和位置。 <template><div i…...
【韩顺平linux】部分上课笔记整理
整理一下一些韩顺平老师上课时候的笔记 课程:【小白入门 通俗易懂】韩顺平 一周学会Linux linux环境:使用阿里云服务器 笔记参考 : [学习笔记]2021韩顺平一周学会Linux 一、自定义函数 基本语法 应用实例: 计算两个参数的和…...
CPP集群聊天服务器开发实践(一):用户注册与登录
目录 1 客户端用户注册与登录 1.1 主要思想 1.2 网络层 1.3 业务层 1.4 数据层 1.5 测试结果 1 客户端用户注册与登录 1.1 主要思想 实现网络层、业务层、数据层的解耦,提高系统的可维护性。 网络层:主要实现对客户端连接、客户端读写请求的捕获…...
C++ Attribute 属性说明符
目录 属性说明符 Attribute编译警告相关[[deprecated]][[maybe_unused]][[fallthrough]][[nodiscard]] 可能触发编译优化[[noreturn]][[likely]]、[[unlikely]][[assume]][[carries_dependency]][[no_unique_address]] 属性说明符 Attribute 属性说明符Attribute自C11起&#…...
Elasticsearch去分析目标服务器的日志,需要在目标服务器上面安装Elasticsearch 软件吗
Elasticsearch 本身并不直接收集目标服务器的日志,它主要用于存储、搜索和分析数据。要收集目标服务器的日志,通常会借助其他工具,并且一般不需要在目标服务器上安装 Elasticsearch 软件,常见的日志收集方案: Filebeat…...
学JDBC 第二日
数据库连接池 作用 使数据库连接达到重用的效果,较少的消耗资源 原理 在创建连接池对象时,创建好指定个数的连接对象 之后直接获取连接对象使用即可,不用每次都创建连接对象 从数据库连接池中获取的对象的close方法真的关闭连接对象了吗…...
Android双屏异显Presentation接口使用说明
在点餐、收银、KTV等场景,对于双屏异显的需求是非常多的,首先可以节省硬件成本。而现在的智能板卡很多运行Android系统,从Android4.2开始支持WiFi Display(Miracast)功能后,就开始支持双屏异显Presentation这套应用层接口了,下面以Android5.1系统来说明这套接口的使用要…...
【Uniapp-Vue3】z-paging插件组件实现触底和下拉加载数据
一、下载z-paing插件 注意下载下载量最多的这个 进入Hbuilder以后点击“确定” 插件的官方文档地址: https://z-paging.zxlee.cn 二、z-paging插件的使用 在文档中向下滑动,会有使用方法。 使用z-paging标签将所有的内容包起来 配置标签中的属性 在s…...
JDK 9新特性学习大纲
第1部分:引言与背景 第1章:JDK 9的诞生与目标 1.1 JDK 9的核心目标与设计哲学 1.2 JDK 9的重要更新概览 1.3 兼容性与升级策略 第2部分:模块化系统(Project Jigsaw) 第2章:模块化基础 2.1 模块化的背景…...
【C语言标准库函数】三角函数
目录 一、头文件 二、函数简介 2.1. 正弦函数:sin(double angle) 2.2. 余弦函数:cos(double angle) 2.3. 正切函数:tan(double angle) 2.4. 反正弦函数:asin(double value) 2.5. 反余弦函数:acos(double value)…...
Redisson全面解析:从使用方法到工作原理的深度探索
文章目录 写在文章开头详解Redisson基本数据类型基础配置字符串操作列表操作映射集阻塞队列延迟队列更多关于Redisson详解Redisson 中的原子类详解redisson中的发布订阅模型小结参考写在文章开头 Redisson是基于原生redis操作指令上进一步的封装,屏蔽了redis数据结构的实现细…...
声明式导航,编程式导航,导航传参,下拉刷新
1.页面导航 1.声明式导航 1.1跳转到tabBar页面 1.2跳转到非tabBar页面 1.2后退导航 、 2.编程式导航 2.1跳转到tabBar页面 2.1跳转到非tabBar页面 2.3后退导航 3.导航传参 3.1声名式导航传参 3.2编程式导航传参 3.3在onLoad中接受参数 4.下拉刷新 4.1回顾下拉刷新…...
金和OA C6 DownLoadBgImage任意文件读取漏洞
金和OA C6 DownLoadBgImage任意文件读取漏洞 漏洞描述 金和C6数据库是一款针对企业信息化管理而设计的高级数据库管理系统,主要应用于企业资源规划(ERP)、客户关系管理(CRM)以及办公自动化(OA)…...
激活函数篇 03 —— ReLU、LeakyReLU、ELU
本篇文章收录于专栏【机器学习】 以下是激活函数系列的相关的所有内容: 一文搞懂激活函数在神经网络中的关键作用 逻辑回归:Sigmoid函数在分类问题中的应用 整流线性单位函数(Rectified Linear Unit, ReLU),又称修正线性单元&a…...
UdpServer
Udp服务端: using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; using System.IO; using …...
PromptSource安装报错
一、现象 运行命令:streamlit run promptsource/app.py 报错: streamlit run promptsource/app.py Traceback (most recent call last): File "/usr/local/bin/streamlit", line 5, in <module> from streamlit.cli import main File …...
前端学习-页面尺寸事件以及阻止默认行为(三十三)
目录 前言 页面尺寸事件 语法 检测屏幕宽度 获取宽高 元素尺寸的位置 总结 示例代码 阻止默认行为 阻止冒泡 语法 阻止冒泡如何做 阻止元素默认行为如何做 总结 前言 晚上好各位 页面尺寸事件 会在窗口尺寸改变的时候触发条件 语法 window.addEventListener(…...
Kafka 入门与实战
一、Kafka 基础 1.1 创建topic kafka-topics.bat --bootstrap-server localhost:9092 --topic test --create 1.2 查看消费者偏移量位置 kafka-consumer-groups.bat --bootstrap-server localhost:9092 --describe --group test 1.3 消息的生产与发送 #生产者 kafka-cons…...
5 计算机网络
5 计算机网络 5.1 OSI/RM七层模型 5.2 TCP/IP协议簇 5.2.1:常见协议基础 一、 TCP是可靠的,效率低的; 1.HTTP协议端口默认80,HTTPSSL之后成为HTTPS协议默认端口443。 2.对于0~1023一般是默认的公共端口不需要注册,1024以后的则需…...
【华为OD机考】华为OD笔试真题解析(1)--AI处理器组合
一、题目描述 某公司研发了一款高性能AI处理器,每台物理设备具备8颗AI处理器,编号分别为0、1、2、3、4、5、6、7。 编号0~3的处理器处于同一链路中,编号4~7的处理器处于另外一个链路中,不同链路中的处理器不能通信,如…...
程序员也可以这样赚钱
最近有朋友和我交流了关于程序员副业的想法,我想借这个机会对目前软件开发常用的兼职平台做一个梳理。 以下是程序员接副业的靠谱平台推荐,结合政策合规性、平台口碑及实际操作性整理,覆盖国内外主流选择: 一、国内综合型平台 程序…...
VMware虚拟机安装、创建Ubuntu虚拟机及汉化设置全流程详细教程
一、安装VMware Workstation 下载VMware 访问官网:https://www.vmware.com 选择适合的版本(如 Workstation Pro 或 VMware Player,后者免费)。完成下载后运行安装程序。 网盘下载: 链接: https://pan.baidu.com/s/1MQ…...
HTTP协议学习大纲
第一阶段:HTTP基础概念 互联网与Web基础 理解Web工作原理:客户端-服务器模型URL与URI的结构及区别端口、协议、域名概念 HTTP协议概览 HTTP的作用与特点(无状态、无连接、可扩展)HTTP协议版本演进(0.9 → 1.0 → 1.1 …...
