C语言之三维数组

article/2025/8/26 11:25:55

前言:


之前学习C语言的时候仅仅是掌握了二维数组,但是并没有对三维数组进行研究,此情此景. . . . . .那就让我对先前研究的三维数组的相关知识发布一下吧,也许能够帮到你!实际上,当你阅读此篇文章时,我假设你已经对C语言的二维数组非常熟悉了,并且非常熟悉数组与指针之间的关系,OK。!


一、三维数组长啥样

在这里插入图片描述
这个图是我网上拷贝的,搞这张图片的大哥,,希望你手下留情,不要打我哦,我胆子小。

二、三维数组的遍历

#include "stdio.h"
void main(){//三维数组int a[2][3][4] ={{{6,2,3,4},{5,11,15,8},{8,20,10,11}},{{0,0,3,4},{5,0,7,8},{8,1,18,31}}};int i, j, k;int(*p)[3][4];					//p是指向二维数组的指针 p = a;							//p指向三维数组的0行 (也即p指向一个二维数组) //p=&a[0];						//与上等价printf("				【三维数组的遍历】:\n\n");for (i = 0; i < 2; i++) {for (j = 0; j < 3; j++) {for (k = 0; k < 4; k++) {
//元素值遍历 : printf("a[%d][%d][%d]=%-9d  ", i, j, k, *(*(*(p + i) + j) + k));//元素值 printf("a[%d][%d][%d]=%-9d  ", i, j, k, *(*(a[i] + j) + k));	//元素值 printf("a[%d][%d][%d]=%-9d  ", i, j, k, *(a[i][j] + k));		//元素值 printf("a[%d][%d][%d]=%-9d  ", i, j, k, *(&a[i][j][k]));		//元素值 printf("a[%d][%d][%d]=%-9d  ", i, j, k, a[i][j][k]);			//元素值 printf("a[%d][%d][%d]=%-9d\n", i, j, k, p[i][j][k]);			//元素值 //元素地址遍历: 
//				printf("&a[%d][%d][%d]=%-9d  ", i, j, k, *(*(p + i) + j) + k);	//元素地址 
//				printf("&a[%d][%d][%d]=%-9d  ", i, j, k, *(a[i] + j) + k);		//元素地址
//				printf("&a[%d][%d][%d]=%-9d  ", i, j, k, a[i][j] + k);			//元素地址
//				printf("&a[%d][%d][%d]=%-9d\n", i, j, k, &a[i][j][k]);			//元素地址//由于少了整体括号,所以出现错误的遍历,只有a[0][0][0],a[0][1][0],a[0][2][0],a[1][0][0],a[1][1][0],a[1][2][0]正确
//
//				printf("a[%d][%d][%d]=%-9d  ", i, j, k, **(*(p + i) + j) + k);	//错误,由于最外层少括号 
//				printf("a[%d][%d][%d]=%-9d  ", i, j, k, **(a[i] + j) + k);		//错误,由于最外层少括号 
//				printf("a[%d][%d][%d]=%-9d  ", i, j, k, *a[i][j] + k);			//错误,由于最外层少括号 
//				printf("a[%d][%d][%d]=%-9d\n", i, j, k, *&a[i][j][k]);			//正确 }printf("\n");}}
}【【【注意】】】:在对地址取*运算时, 一定要加“()”,也即对地址整体取“括号”,然后*,否则将得不到期望值!
比如:*(a[i] + j) + k表示 & a[i][j][k]  ====> 也即元素a[i][j][k]的地址。对其取*运算应该是*(*(a[i] + j) + k) ====> 表示是元素a[i][j][k]的值。而不能写成这样:**(a[i] + j) + k ====> 并不是我们期望的各元素值!当然了:对于&a[i][j][k]求其元素值时,可以对其直接取*,不必在意()整体的问题!【分析如下】:
for (i = 0; i < 2; i++) {for (j = 0; j < 3; j++) {for (k = 0; k < 4; k++) {printf("a[%d][%d][%d]=%-9d  ", i, j, k, **(a[i] + j) + k);		//**(*(p + i) + j) + k, *a[i][j] + k同理}}
}【输出结果】:[6],7,8,9  [5],6,7,8  [8],9,10,11  [0],1,2,3  [5],6,7,8  [8],9,10,11
【很明显】:只有a[0][0][0], a[0][1][0], a[0][2][0], a[1][0][0], a[1][1][0], a[1][2][0]正确,其他值并不是个元素的值!
【假如】:i=0, j=0, k=0; ==> **(a[i]+j)+k ===> **(a[0]+0)+0 ===> *(*(a[0]+0))+0 ===> *(a[0][0])+0 ===>*(a[0][0])+0 ===> *(&a[0][0][0])+0= 6i=0, j=0, k=1; ==> **(a[i]+j)+k ===> **(a[0]+0)+1 ===> *(*(a[0]+0))+1 ===> *(a[0][0])+1 ===>*(a[0][0])+1 ===> *(&a[0][0][0])+1= 7i=0, j=0, k=2; ==> **(a[i]+j)+k ===> **(a[0]+0)+2 ===> *(*(a[0]+0))+2 ===> *(a[0][0])+2 ===>*(a[0][0])+2 ===> *(&a[0][0][0])+2= 8i=0, j=0, k=3; ==> **(a[i]+j)+k ===> **(a[0]+0)+3 ===> *(*(a[0]+0))+3 ===> *(a[0][0])+3 ===>*(a[0][0])+3 ===> *(&a[0][0][0])+3= 9
其他情况亦是如此!

三、看下三维数组中的地址系列

#include "stdio.h"
void main()
{int a[2][3][4] =	{{{6,2,3,4},{5,11,15,8},{8,20,10,11}},{{0,0,3,4},{5,0,7,8},{8,1,18,31}}};
//相当于有2个二维数组a[0] [3][4]和a[1] [3][4]------其中a[0]和a[1]为二维数组名! //【【【第1个二维数组】】】:				【a[0]相当于二维数组的名字】printf("【第1个二维数组】:\n");printf("0行首地址: %d\n", &a[0][0]);	//0行首地址	(a[0][0]相当于一维数组的名字) 【int (*)[4]型】printf("1行首地址: %d\n", &a[0][1]);	//1行首地址	(a[0][0]相当于一维数组的名字) 【int (*)[4]型】printf("2行首地址: %d\n\n", &a[0][2]);	//2行首地址 (a[0][0]相当于一维数组的名字) 【int (*)[4]型】printf("0行首地址: %d\n", a[0]);		//0行首地址	(a[0][0]相当于一维数组的名字) 【int (*)[4]型】printf("1行首地址: %d\n", a[0] + 1);	//1行首地址 (a[0][0]相当于一维数组的名字) 【int (*)[4]型】printf("2行首地址: %d\n\n", a[0] + 2);	//2行首地址 (a[0][0]相当于一维数组的名字) 【int (*)[4]型】printf("0行0列首地址: %d\n", a[0][0]);		//0行0列元素地址 (a[0][0]相当于一维数组的名字) printf("1行0列首地址: %d\n", a[0][1]);		//1行0列元素地址 (a[0][0]相当于一维数组的名字)printf("2行0列首地址: %d\n\n", a[0][2]);	//2行0列元素地址 (a[0][0]相当于一维数组的名字) printf("0行0列元素地址: %d\n", &a[0][0][0]);//0行0列元素地	(a[0][0]相当于一维数组的名字) printf("1行0列元素地址: %d\n", &a[0][1][0]);//1行0列元素地	(a[0][0]相当于一维数组的名字) printf("2行0列元素地址: %d\n\n", &a[0][2][0]);//2行0列元素地址(a[0][0]相当于一维数组的名字) printf("0行0列元素值: %d\n", a[0][0][0]);//0行0列元素值	(a[0][0]相当于一维数组的名字) printf("1行0列元素值: %d\n", a[0][1][0]);//1行0列元素值	(a[0][0]相当于一维数组的名字) printf("2行0列元素值: %d\n\n", a[0][2][0]);//2行0列元素	(a[0][0]相当于一维数组的名字) printf("0行0列元素值: %d\n", *(*(a[0] + 0) + 0));//a[0][0][0]   (a[0]相当于二维数组的名字) printf("1行0列元素值: %d\n", *(*(a[0] + 1) + 0));//a[0][1][0]    (a[0]相当于二维数组的名字) printf("2行0列元素值: %d\n\n", *(*(a[0] + 2) + 0));//a[0][2][0]  (a[0]相当于二维数组的名字) //【【【第2个二维数组】】】:				【a[1]相当于二维数组的名字】printf("\n【第2个二维数组】:\n");printf("0行首地址: %d\n", &a[1][0]);	//0行首地址		【int (*)[4]型】printf("1行首地址: %d\n", &a[1][1]);	//1行首地址		【int (*)[4]型】printf("2行首地址: %d\n\n", &a[1][2]);	//2行首地址	    【int (*)[4]型】printf("0行首地址: %d\n", a[1]);		//0行首地址 【int (*)[4]型】printf("1行首地址: %d\n", a[1] + 1);	//1行首地址 【int (*)[4]型】printf("2行首地址: %d\n\n", a[1] + 2);	//2行首地址 【int (*)[4]型】printf("0行0列元素地址: %d\n", &a[1][0][0]);//0行0列元素地址printf("1行0列元素地址: %d\n", &a[1][1][0]);//1行0列元素地址printf("2行0列元素地址: %d\n\n", &a[1][2][0]);//2行0列元素地址printf("0行0列元素值: %d\n", a[1][0][0]);//0行0列元素值 printf("1行0列元素值: %d\n", a[1][1][0]);//1行0列元素值 printf("2行0列元素值: %d\n\n", a[1][2][0]);//2行0列元素值 printf("0行0列元素值: %d\n", *(*(a[1] + 0) + 0));//0行0列元素值 printf("1行0列元素值: %d\n", *(*(a[1] + 1) + 0));//1行0列元素值 printf("2行0列元素值: %d\n", *(*(a[1] + 2) + 0));//2行0列元素值 printf("2行2列元素值: %d\n", *(*(a[1] + 2) + 2));//2行2列元素值 //【【【其他问题】】】:printf("\n\n【其他问题】:");printf("\na[0]= %d\n", a[0]);		//指向一维数组的指针    int (*)[4]型(a[0]是二维数组名)printf("a[1]= %d\n\n", a[1]);		//指向一维数组的指针    int (*)[4]型(a[1]是二维数组名)printf("&a[0]= %d\n", &a[0]);		//指向二维数组的指针    int (*)[3][4]型(a[0]是二维数组名)printf("&a[1]= %d\n", &a[1]);		//指向二维数组的指针    int (*)[3][4]型(a[1]是二维数组名)printf("&a[0]+1= %d\n\n", &a[0] + 1);//指向二维数组的指针	int (*)[3][4]型printf("a= %d\n", a);				//指向二维数组的指针    int (*)[3][4]型(a是三维数组名)printf("a+1= %d\n", a + 1);			//指向二维数组的指针    int (*)[3][4]型printf("\n");
}

四、详解三维数组


//首先我们回顾一下二维数组:
二维数组是“数组的数组”,也即二维数组是由一维数组构成的!【例如】:int a[3][4];
二维数组是数组的数组,也即二维数组a是3个一维数组组成的!
相当于定义了3个一维数组:int a[0][4], a[1][4], a[2][4];
此处是把a[0], a[1], a[2]看作是一维数组的名字。
则二维数组就转换成一维数组了,就可以在一维数组的领域内解决问题了!且在二维数组中存在以下等式关系:
a[i]+j === *(a+i)+j === &a[i][j]				//这是各元素地址
* (a[i]+j) === *(*(a +i)+j) === *(&a[i][j])	//这是各元素值且在指向一维数组的指针中存在以下关系:(int(*p)[4])
int(*p)[4];//指向一维数组的指针
若p = a;	//使得p指向二维数组0行首地址
则有以下等式必然成立:
*(p+i)+j === *(a+i)+j === a[i]+j === &a[i][j]	//这是各元素地址
**(p+i)+j) === **(a+i)+j) === *(a[i]+j) === * &a[i][j] ===p[i][j]	//这是各元素值//再来看三维数组:
通过对二维数组的理解,我们可以知道,三维数组是“数组的数组的数组”
也即三维数组是由二维数组构成的,而二维数组又是由一维数组构成的!
例如 int a[2][3][4];
可以把三维数组看成是2个二维数组构成的,而二维数组又是由一维数组构成的!
也即本题定义了2个二维数组int a[0][3][4]和a[1][3][4](此处是把a[0], a[1]看作是二维数组的名字)!
而二维数组又是由一维数组构成的,所以此处的二维数组又是由3个一维数组构成的。
也即一维数组a[0][0][4], a[0][1][4], a[0][2][4], a[1][0][4]......!(此处把a[0][0], a[0][1], a[0][2]......看作是一维数组的名字)
也即int a[0][0][4]		//此处是把a[0][0]看做是一维数组的名字,它包含4个元素int a[0][1][4]		//此处是把a[0][1]看做是一维数组的名字,它包含4个元素int a[0][2][4]		//此处是把a[0][2]看做是一维数组的名字,它包含4个元素int a[1][0][4]		//此处是把a[1][0]看做是一维数组的名字,它包含4个元素int a[1][1][4]		//此处是把a[1][1]看做是一维数组的名字,它包含4个元素int a[1][2][4]		//此处是把a[1][2]看做是一维数组的名字,它包含4个元素有了上述的理解之后,我们就可以对三维数组进一步认识了!
在二维数组中存在以下等式关系:
a[i] + j === *(a + i) + j === &a[i][j](等式一)		//这是各元素地址
上述说了:三维数组是由二维数组构成的,也即上述三维数组是由2个二维数组a[0]和a[1]构成。
对(等式一)进行替换:
在(等式一)中:
a[i] + j === > a为二维数组的名字, i和j分别为二维数组的行和列;
所以在三维数组中:a[i][j] + k === > a[i]为二维数组的名字,j和k为相应的二维数组的行和列。
那么由此在三维数组中的等式也出现了:a[i] + j === *(   a + i) + j === &   a [i][j](a为二维数组的名字, i和j分别为二维数组的行和列)
a[i][j] + k === *(a[i] + j) + k === & a[i][j][k](a[i]为二维数组的名字, j和k分别为二维数组的行和列)*********************************************************************************************
【【【指向二维数组的指针】】】:同样,在二维数组中,存在指向一维数组的指针int(*p)[];
那么在三维数组中,同样存在指向二维数组的指针int(*p)[][];【【【在二维数组中】】】:
int(*p)[4];//指向一维数组的指针
若p = a;	//使得p指向二维数组0行(实际上也就是p指向一维数组)
则有以下等式必然成立:
*(p + i) + j == = *(a + i) + j == = a[i] + j == = &a[i][j]	//各元素地址(a表示二维数组的名字,i,j是相应的行和列)* (*(p + i) + j) == = *(*(a + i) + j) == = *(a[i] + j) == = *(&a[i][j])//这是各元素值【【【同样在三维数组中】】】:int(*p)[3][4];	//指向二维数组的指针
如若p = a;		//p指向三维数组的0行(实际上也就是p指向二维数组)
则同样有以下等式成立:*(*(p + i) + j) + k) == = *(a[i] + j) + k == = a[i][j] + k == = &a[i][j][k]	//这是各元素地址(a[i]表示二维数组的名字,j,k是相应的行和列)* (*(*(p + i) + j) + k)) == = *(*(a[i] + j) + k) == = *(a[i][j] + k) == = *&a[i][j][k] == = a[i][j][k]	//这是各元素地址同样在对地址取“*”运算符(求元素值)时,要对地址整体取“()”,然后“*”,这和二维数组中的道理是一样的!
在三维数组中,始终要知道a[i]代表二维数组名!这样就可以在二维数组的基础之上照猫画虎了!

温馨提示:由于时间关系,运行结果我就不贴图了,程序是完全正确的,大家拿去自己运行,这都是我先前学习C语言的时候做的笔记,均为原创,直到今天才发出来,希望能够帮助到你!


http://chatgpt.dhexx.cn/article/o3vhXfdc.shtml

相关文章

Cassandra的Allow Filtering

https://blog.csdn.net/qiaojialin/article/details/81709766 Cassandra 支持的查询语句很严格&#xff0c;首先 partition key 必须精确查询 缺少 partition key 的等值过滤条件时就需要加Allow Filetering

All matches were filtered out by modular filtering for argument: mysql-community-server

问题场景&#xff1a;在尝试使用docker安装自定义centos环境时&#xff0c;使用dockfile文件创建镜像容器&#xff0c;在centos7镜像中使用yum安装mysql5.7。 在安装过程中报错&#xff1a;All matches were filtered out by modular filtering for argument: mysql-community…

论文笔记:Collaborative Filtering Recommender Systems

学习随笔&#xff0c;仅记录值得留意的点。 协同过滤的适用领域 PS&#xff1a;适用协同过滤的场景并不一定完全符合下述要求&#xff0c;只是如果下述条件符合会更适合使用CF 数据要求 1、item数量足够多 2、每个item有足够多的评分 3、每个用户对较多数量的item给出了评…

【科研导向】Neural Collaborative Filtering 神经协同过滤 <论文理解代码分析>

Neural Collaborative Filtering——WWW17 文章简介一.摘要解析二.技术要点三.实验部分 代码解析一.模型构建二.难点问题 未来展望 文章简介 该文由何向南教授团队于17年发表在IW3C2&#xff0c;其核心思想在于结合了传统矩阵分解的易用性与神经网络对用户项目交互的高维感知力…

Maven Filtering和Profile

Filtering Filtering是resource插件的功能&#xff0c;作用是将资源文件中的占位符替换成对应的值&#xff0c;如下图所示&#xff1a; 使用注意 启用resource插件的filtering功能&#xff1b;设定资源文件的路径、过滤条件和编码等等&#xff1b;设定属性文件来源&#xf…

Particle Filtering粒子滤波

粒子滤波理论 本文由最基础的贝叶斯估计开始介绍&#xff0c;再引出蒙特卡罗采样&#xff0c;重要性采样&#xff0c;SIS粒子滤波&#xff0c;重采样&#xff0c;基本粒子滤波Generic Particle Filter&#xff0c;SIR粒子滤波&#xff0c;这些概念的引进&#xff0c;都是为了解…

Deep Frequency Filtering for Domain Generalization论文阅读笔记

这是CVPR2023的一篇论文&#xff0c;讲的是在频域做domain generalization&#xff0c;找到频域中generalizable的分量enhance它&#xff0c;suppress那些影响generalization的分量 DG是一个研究模型泛化性的领域&#xff0c;尝试通过各自方法使得模型在未见过的测试集上有良好…

读Multiscale Vessel Enhancement Filtering笔记

1.Introduction 通常对CTA和MRA的血管成像方法中&#xff0c;可视化3维血管结构的方法是MIP。但是MIP的缺点是会将非血管结构展现出来并且对于对比度比较低的小血管展现不太好。这也正是TOF-MRA的缺点。 这篇论文的目的是为了增强血管结构&#xff0c;最终达到血管分割的目的。…

Maven Resources Plugin的Filtering功能的Bug

Maven Resources Plugin的Filtering功能是非常强大的&#xff0c;是构建中非常重要的一项必备特性&#xff0c;但是最近发现了Filtering在过滤复杂文本&#xff08;例如Shell脚本&#xff09;时&#xff0c;会有一些问题&#xff0c;简单记录一下。以下是一段Shell脚本&#xf…

Neural Collaborative Filtering

目录 2 PRELIMINARIES3. NEURAL COLLABORATIVE FILTERING3.1 通用框架3.2 广义矩阵分解(GMF)3.3 MLP3.4 GMF和MLP的聚合—NeuMF 4 EXPERIMENTSQ1 NCF的效果比其他SOTA的隐式CF好吗&#xff1f;Q2 带有负采样的log loss&#xff08;这个优化框架&#xff09;是否适用于RS&#x…

图像处理中滤波(filtering)与卷积(convolution)的区别

图像处理中滤波和卷积是常用到的操作。很多人认为卷积就是滤波&#xff0c;两者并无区别&#xff0c;其实不然。两者在原理上相似&#xff0c;但是在实现的细节上存在一些区别。这篇博文主要叙述这两者之间的区别。 1、滤波&#xff08;或者叫相关&#xff09; 简单来说&#…

MAVEN中filtering的作用是什么?

FILTERING的作用 MAVEN提供了一种过滤机制&#xff0c;这种机制能够在资源文件被复制到目标目录的同时&#xff0c;当filtering true时替换资源文件中的占位符&#xff1b;当filtering false时不进行占位符的替换。 写在前面 如下所示filtering的用法&#xff0c;它对资源…

在Navicat premium上实现两个数据库同步

问题&#xff1a;有两个数据库A和B,现需要将数据库A的数据同步到数据库B上。 法一&#xff1a;使用备份 首先在数据库A上生成备份 然后将数据库A的备份拷贝到存储数据库B的备份的文件夹下&#xff0c;然后点击还原备份 方法二:使用工具中的同步操作 点击工具->结构同步 选…

Otter实现数据库同步

Otter工作原理 原理描述: 基于Canal开源产品,获取数据库增量日志数据。典型管理系统架构,manager(web管理)+node(工作节点) a. manager运行时推送同步配置到node节点 b. node节点将同步状态反馈到manager上基于zookeeper,解决分布式状态调度的,允许多node节点之间协同工作…

【愚公系列】2022年12月 数据库-关系数据库同步框架Dotmim.Sync的使用

文章目录 前言1.移动智能的三种模式2.混合模式模式的瓶颈3.数据同步的解决方案 一、关系数据库同步框架Dotmim.Sync的使用1.包介绍2.SqlServer到Sqlite同步测试基本使用3.SqlServer到Sqlite同步测试Web API代理3.1 服务端设置SqlServer同步服务3.2 客户端设置SqlLite同步服务3.…

django mysql数据同步_[django自动同步数据库]Django数据库同步操作技巧详解

同步数据库&#xff1a; 使用上述两条命令同步数据库 1.认识migrations目录&#xff1a; migrations目录作用&#xff1a;用来存放通过makemigrations命令生成的数据库脚本&#xff0c;里面的生成的脚本不要轻易修改。 要正常的使用数据库同步的功能&#xff0c;app目录下必须要…

SQLSERVER数据库同步

SQLSERVER数据库数据同步 1、实现方法2、实现发布3、实现订阅 1、实现方法 使用的是SQL自带的发布与订阅功能&#xff0c;SQL Server版本是2008&#xff0c;一台发布&#xff0c;一台订阅&#xff0c;实现数据库的数据同步。 2、实现发布 打开复制&#xff0c;右击本地发布&…

MySQL第九讲 MySQL集群主从搭建指定数据库同步数据

1、全库同步与部分同步 之前提到&#xff0c;我们目前配置的主从同步是针对全库配置的&#xff0c;而实际环境中&#xff0c;一般并 不需要针对全库做备份&#xff0c;而只需要对一些特别重要的库或者表来进行同步。那如何 针对库和表做同步配置呢&#xff1f; 首先在Master端…

Python数据库同步神器(一键同步)

1.背景&#xff1a; 最近公司项目的用户中心模块经常出现线上问题&#xff0c;测试人员需要将线上真实数据导入到测试环境中去复现Bug。公司有3套测试环境&#xff0c;来回切换并校验数据比较麻烦&#xff0c;于是就有了如下的数据库同步神器。 2.界面&#xff1a; 3.源代码&…