二维数组与二级指针是好朋友吗?

news2025/7/16 2:44:17

抛出问题

有一个二维数组,我想把它传给一个函数。于是我把函数接口定义出来了,如下:

int array[2][3] = {1,2,3,4,5,6};
void fun(int **array) {
  array[0][0] = 5;
}

当我试图直接把数组名传给函数时候,fun(array)编译会报错,大概意思就是类型不匹配。既然类型不匹配,那我就直接强转成你所需要的类型,于是我又做了调整,fun((int **)array),这下确实不报错了。但是此时我还没意识到问题的严重性。不出意外的情况下意外还是发生了,只要进入到这个函数后,程序就挂了。那你知道是什么原因吗?如果不清楚就往下看吧...

指针

先从指针说起,指针是一个特殊的变量, 它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型、 指针所指向的类型 、 指针的值或者叫指针所指向的内存区、 指针本身所占据的内存区

指针的类型

只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。例如:

int*ptr;      //指针的类型是 int*
char*ptr;     //指针的类型是 char*
int**ptr;     //指针的类型是 int**
int(*ptr)[3]; //指针的类型是 int(*)[3]

指针所指向的类型

只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉, 剩下的就是指针所指向的类型。例如:

int*ptr;      //指针所指向的类型是 int
char*ptr;     //指针所指向的的类型是 char
int**ptr;     //指针所指向的的类型是 int*
int(*ptr)[3]; //指针所指向的的类型是 int()[3]

指针的值

在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。

指针本身所占用的内存大小

意思是指针本身占了多大的内存,在32位平台里,指针本身占据了4个字节的长度。可以使用sizeof(指针的类型)测试。

一维数组

对于一个一维数组int array[10],数组名代表一个常量地址,该地址指向第一个元素。以下两种情况数组名不能当指针使用。

&

对数组名取址,int *p_array = &array,&这个运算符也很有讲究的,暂时不多说了。

sizeof

sizeof(array)计算的是整个数组在内存中所占用的空间。

二维数组

二维数组本质上是以数组作为数组元素的数组,即“数组的数组”。假设我们定义了一个二维数组int array[2][3] = {1,2,3,4,5,6}

网上有很多地方都再说数组名arrayarray[0]&array[0]以及&array[0][0]是等效的。那我们代码测试一下。

printf("%#x,%#x,%#x,%#x\r\n",array,array[0],&array[0],&array[0][0]);

Terminal:
0x404008,0x404008,0x404008,0x404008

因为这几种写法输出地址都是相同的,所以有的同学自然就认为这几种写法就是一样的。虽然地址相同,但是实际意义是有区别的,我们继续看下面的代码。

//这里重新定义了指针变量,能够方便的知道右值得类型
int *p_array1 = array[0];
int *p_array2 = &array[0][0];
int (*p_array3)[3] = &array[0];
int (*p_array4)[3] = array;
  
printf("%#X,%#X,%#X,%#X,%#X\r\n",array,++p_array1, ++p_array2, ++p_array3, ++p_array4);

Terminal:
0X404008,0X40400C,0X40400C,0X404014,0X404014

根据以上实验分析能够看出:array[0]&array[0][0]指针类型相同,都是int *,地址存放的是int数据,当指针自增1时地址都偏移了一个int类型的大小。

&array[0]array指针类型相同,都是int (*)[3],首先它是一个数组指针,这个指针指向一个数组,数组中数据的类型为int型。当指针自增1时地址都偏移了一个数组的长度(即3个int数据的大小)。

所以说array只和&array[0]真正意义等效。那怎么去理解这几种表达呢 ?

表示

含义
array是一个数组指针,类型为int (*)[3]。指向二维数组中第一个元素(元素是一维数组),指针所指向的内存大小为一维数组的长度
array[0]是一个指针,类型为int *。就相当于一个一维数组名,指向一维数组中第一个元素的地址,指针所指向的内存大小为一个数据长度
&array[0]是一个数组指针,类型为int (*)[3]。相当于对一维数组取地址。指针所指向的内存大小为一维数组的长度
&array[0][0]是一个指针,类型为int *,是对二维数组中第一个数据取地址,注意是数据不是元素,指针所指向的内存大小为一个数据长度

如以上能够理解清楚,那么文中的问题应该就能够自己分析清楚了。

二级指针

先定义一个二级指针int **p,首先p是一个指针,在这个地址中存放的数据是指向一个整形数据的地址。

问题解答

接着看文章中的问题,把一个二维数组强转成二级指针传给了函数。注意二维数组名的类型是一个数组指针二级指针完全不是一个东西。那么会出现什么问题呢?

int array[2][3] = {1,2,3,4,5,6};

int main(int argc ,char **argv) {
  int **p_data = (int **)array;
  printf("%#x, %d\r\n", p_data, *p_data);
}

Terminal:
0x404008, 1
地址数据
0x4040081
0x40400C2
0x4040103
0x4040144
0x4040185
0x40401C6

看上面的例子,array的地址为0x404008,当把一个二维数组强转成二级指针的时候。p_data地址中存放的数据为1,因为二维数据中第一个数据就是1。根据二级指针的定义,这个数据1又会当成一个地址,该地址指向的内存才是最终的数据。

但是呢,这个地址1其实是个数据,并不是真正的地址。如果访问地址1中的数据,就属于非法访问地址了,可能会进入异常。

二维数据当函数入参

通过以上学习我们已经知道二维数组名就是一个数组指针,我们函数就可以像下面这样声明。

void fun(int array[][3], int row);
void fun(int (*p_array)[3], int row);
void fun(int row, int column, int array[row][column]);

实参与入参

最后在看下,应该如何定义与实参相对应的形参的数据类型。

含义实参形参
二维数组(数组的数组)int array[4][6]int (*array)[6]
指针数组(数组中的数据是指针)int *array[6]int **array
数组指针(指向数组的指针)int (*array)[6]int (*array)[6]
二级指针(指针的指针)char **arraychar **array

点击链接加入群聊【C语言/C++编程学习基】:小鱼快来啊的个人空间-小鱼快来啊个人主页-哔哩哔哩视频哔哩哔哩小鱼快来啊的个人空间,提供小鱼快来啊分享的视频、音频、文章、动态、收藏等内容,关注小鱼快来啊账号,第一时间了解UP注动态。编程学习群:724050348 每天分享一个编程小游戏~C/C++游戏源码素材及各种安装包, 私信不常看!https://space.bilibili.com/1827181878?spm_id_from=333.1007.0.0

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

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

相关文章

C++ 中explicit的作用及用法

目录 Cexplicit(官网的说法) Cexplicit 清楚的说法(建议英文不好的从这里开始食用哦) Cexplicit使用的例子(建议喜欢自己敲代码实验的从这里开始食用哦) 总结 Cexplicit(官网的说法&#xff…

神经网络和深度学习-梯度下降Gradient Descent(下)

梯度下降gradient descent 我们接着用数学公式来看一下梯度下降 首先是梯度 ∂cos⁡t∂ω\frac{\partial \cos t}{\partial \omega} ∂ω∂cost​ 然后我们对权重进行更新 ωω−α∂cos⁡t∂ω\omega\omega-\alpha \frac{\partial \cos t}{\partial \omega} ωω−α∂ω∂c…

python基础之容器类型公共方法

文章目录一、简介1.高级数据类型的公共方法:2.python的内置函数:3.python包含的内置函数:二、遍历字典的列表一、简介 1.高级数据类型的公共方法: 列表元组字典或者字符串都能够共同使用的方法; 2.python的内置函数…

【kafka】七、kafka数据可靠性保证

数据可靠性保证 为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack&#xff0c…

基于AntBlazor的学生在线练习系统实现过程的简单总结

前言 疫情当前,作为一名教师,如何能让学生进行有效练习成了一个难题。如果使用 “问卷星”等线上调查问卷式的网站来做练习测试,是无法及时有效的进行,更谈不上对学生练习情况进行跟踪。鉴于以上现实情况和问题&#xff…

神经网络和深度学习-用pytorch实现线性回归

用pytorch实现线性回归 用pytorch的工具包来实现线性模型的训练过程 准备数据集 设计模型 构造损失函数和优化器(使用pytorch API) 训练过程:前馈、反馈、更新 准备数据 在PyTorch中,计算图是小批处理的,所以X和…

MATLAB | 世界杯来用MATLAB画个足球玩叭~

世界杯教你用MATLAB画个超逼真的足球, 需要准备Partial Differential Equation Toolbox工具箱,同时因为用到了polyshape类所以至少需要R2017b版本。 绘制讲解 数据来源及说明 我是真的不想写注释了太麻烦了,给大家讲一下我的思路希望能够看…

判断一个时间段是否经过了另一个时间段

场景: IOT设备存在离线与恢复时间记录,每一次离线和恢复记为一个周期即一条数据, 现在需要统计出在某个时段存在离线记录的数据,如果目前未恢复,没有恢复时间,恢复时间置为9999-01-01 00:00:00 原始数据如下(t_offline_record):…

OPengl学习——初识opengl

文章目录1、网站罗列2、一些词语解析1.入门概念2.Opengl Vs DirectX3、QOpenGLWidget4、引用**OpenGL(Open Graphics Library)*是一个跨编程语言、跨平台的编程图形程序接口,它将计算机的资源抽象称为一个个OpenGL的对象,对这些资…

3.5、点对点协议 PPP

3.5、点对点协议 PPP 3.5.1、基本介绍 点对点协议 PPP(Point-to-Point Protocol) 是目前使用最广泛的点对点数据链路层协议。 用户计算机只有获取到 ISP 所分配的合法 IP 地址后,才能成为因特网上的主机 用户计算机与 ISP 进行通信时,所使用的数据链路…

【学习笔记21】JavaScript数组的基本方法

笔记首发 一、push:末位新增 语法: 数组.push(数据)作用: 向数组末尾添加数据返回值: 追加数据后, 数组最新的长度 var arr [10, 20, 30, 40];console.log(原始数组: , arr); ​var num arr.push(500);console.log(push新增后数组: , arr);console.log(push的返回…

进程的初识

目录预备知识 -> 操作系统操作系统的定义操作系统的定位进程进程的概念进程调度的过程进程的管理描述组织PCB描述进程的特征进程调度的相关属性进程的状态优先级上下文记账信息预备知识 -> 操作系统 操作系统的定义 操作系统是一个搞管理的软件 对上,要对硬…

为什么一定要申请专利呢

问题一:如何避免被判为非正常专利? 原本有个发明创造,想申请专利,却被列入非正常申请,甚至违法。以下五种情况一定要注意,千万不能碰。 1、同一单位或个人申请多件明显抄袭现有技术的专利; 2…

如何通过 Java 合并和取消合并 Excel 单元格

在整理 Excel 中的数据时,我们不可避免地需要合并和取消合并单元格。同时,如果需要创建跨列或行的标题,我们可以合并 Excel 单元格以在电子表格中轻松完成此操作。 合并单元格是指将两个或多个单元格合并为一个单元格,而取消合并单…

4.4——数据库和前后端身份认证

目录数据库与身份认证数据库的基本概念什么是数据库常见的数据库及分类传统型数据库的数据组织结构安装并配置 MySQL使用 MySQL Workbench 管理数据库连接数据库了解主界面的组成部分创建数据库创建数据表向表中写入数据使用 SQL 管理数据库在项目中操作 MySQL安装与配置 mysql…

华为机试 - 数大雁

目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 一群大雁往南飞,给定一个字符串记录地面上的游客听到的大雁叫声,请给出叫声最少由几只大雁发出。 具体的: 1.大雁发出的完整叫声为”quack“,因为有多只大雁同一时…

怎么还不会进制转换,进来学。

✨进制转换,深度讲解✨特殊的十进制转换(整数)一、十进制转换为其他进制二、其他进制转换为十进制二进制的特殊转换(整数)一、二进制转换为其他进制二、其他进制转换为二进制任意进制的转换(整数&#xff0…

Java项目:JSP高校新生报到迎新管理系统

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 辅导员角色包含以下功能: 辅导员登录,学生留言管理,新生报到管理,宿舍分配管理等功能。 财务管理角色包含以下功能: 财务…

一道Android题目逆向动态调试

题目来源于海淀区网络与信息安全管理员大赛,题目中将加密验证算法打包进.so,在程序中动态调用check。 本题目通过System.loadLibrary(“native-lib”)加载了libnative-lib.so文件,该文件通过jeb可以实现提取 图1 题目关键代码 调试环境选择…

【矩阵论】4.矩阵运算——广义逆——定义性质与特殊矩阵的广义逆

4.3 广义逆 4.3.1 定义 若mn矩阵AAmn与矩阵XXnm满足四个条件①AXAA,②XAXX,③(AX)HAX,④(XA)HXA则X为A的加号逆(广义逆),记为XA\begin{aligned} &若 m\times n 矩阵AA_{m\times n} 与矩阵 XX_{n\times m} 满足四个条件\\ &①AXAA,\quad ② XAXX…