前言:
之前学习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]代表二维数组名!这样就可以在二维数组的基础之上照猫画虎了!