那年我不懂的二维数组
二维数组的定义:
1--int a[2][3]//两行三列的数组
二位数组的初始化:
1--int a[2][3]={1,2,3,4,5,6}-----相当于
1 2 3
4 5 6
- -int a[2][3]={{1 ,2 }{3}};----相当于
1 2 0
3 0 0
- -int a[][3]={{1 ,2 }{4 , 5}}---相当于
1 2 0
4 5 0
- -int a[][3]={1 ,2 ,3 ,4 ,5}---相当于
1 2 3
4 5 0
因此定义二维数组时候,行数可以省略 列数一定不可以省略
列数省略的话第三四种情况就不知道该如何初始化
如何理解二维数组
接下来该讲讲我们该如何理解二维数组,想必大家在学习的时候一定也会头疼吧!
Int d[3][4];
我么从内往外看,先看最内层d[3],把它看作一维数组
d[0] d[1] d[2]
每个一维数组也是一个数组
d[0][0] d[0][1] d[0][2] d[0][3]
d[1][0] d[1][1] d[1][2] d[1][3]
d[2][0] d[2][1] d[2][2] d[2][3]
d[0] d[1] d[2]也就相当于这三个数组的数组名
也就是d[0]=&d[0][0]=*p,*(p+1)=d[1]=&d[1][0]这样就容易理解多了
二维数组的函数传参
好了,既然大家都理解到这了,那么接下来让我们来探索一下二维数组的函数传参
这里想要理解明白就需要大家对指针和二维数组的存储方式有所了解
1--把二维数组转化为一维数组,传参只传二维数组的首地址。
int a[2][3] = {1,2,3,4,5,6};
int i=1,j=2;
void arraytwo(int n, int* p) {
int* end;
end = p + n - 1;
cout << *(p + i * 3 + j) << endl;//相当于输出a[1][2]
}
int main() {
arraytwo(6, &a[0][0]);
arraytwo(6, (int*)a);
arraytwo(6, *a);
arraytwo(6, (int*)&a);//类型不同不能传参,应进行强制类型转换
int* p = (int*)a;
int* p1 = (int*)(a + 1);
int* p2 = *a;
int* p3 = (int*) & a;
cout << p << endl;
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
//*a=*(a+0)+0=&a[0][0];也就是二维数组的首地址*a=a[0]=&a[0][0];(int*)
//二维数组的数组名a,是指向长度为3数组的指针(int (*)[3]),a=&a[0];
// 数值上等于每行首元素的地址,a+1,地址跳越了一个数组的长度
//&a整个数组的地址(int*[2][3])
return 0;
运行结果:
6
6
6
6
00007FF64F1EC000
00007FF64F1EC00C
00007FF64F1EC000
00007FF64F1EC000
下面总结一下:对于一个二维数组a[2][3]
*a=a[0]相当于a[0][3]的数组名,类型为int *;
a=&a[0]是整个数组的地址,类型为int*[3];
&a是整个二维数组的地址,类型为int *[2][3];
2--把二维数组的行指针进行传参,然后逐一获取每行的数据。
这种方式也就相当于将二维数组传给二维数组
int a[2][3] = {1,2,3,4,5,6};
void arrayrow(int (*a)[3],int n) {
//a是指向一行数组的指针,可以通过a+1获取第二行的地址,这样便可以遍历完整数组。
cout << *(a + 1) + 2 << endl;//输出a[1][2]
}
//也可以是int a[][3],因为(a*)[3]也就相当于a[][3];
void arrayrow(int a[][3],int n){
cout << *(a + 1) + 2 << endl;//输出a[1][2]
}
//列数一定不可以省略就跟定义初始化一样,函数的传参就相当于数组的初始化
//列数不知道,给你传参的一串数字,你怎么按行去分割。
arrayrow(a, 3);
好,刑,ok!看到这里如果你还是无法理解的话,
我们再结合一下一维数组的传参方式来瞧瞧!
我们有一维数组a[4]={1,2,3,4};
Void arrayone(int a[],int n){//n是一维数组的长度
for(int i=0;i<n;i++){
cout<<a[i];
}
}
Void arrayone(int *a,int n){//n是一维数组的长度
for(int i=0;i<n;i++){
cout<<*(a+i);
}
}
致敬能看到这里的每一个boy or girl
上面讲到的只是两种基本的二维数组的传参,日常足够用了。
当然还有一种方式二维数组的指针转化为二维指针传参,一般是动态的内存分配,作者还没进行研究。
地址计算
别急!
说到二维数组必然还是少不了地址的计算。
二维数组的储存结构有两种:按列储存和按行储存
在按行储存的方式中,数组的元素是按行进行存储的。也就是在一行上,元素的地址是连续的。
对于数组int a[4][5](行数为4,列数为5),元素a[i][j]可以通过下面的公式计算得到
a[i][j]=baseaddress(&a[0][0])+(i*5(列数)+j)*元素单位大小.
例如:对于int a[4][5]的首地址为196
- -a[2][3]=196+(2*5+3)* 4= 196 + 13*4 = 196 + 52 = 248
这种按行储存的方式是C++/c java 和python默认的储存方式
按列储存的方式,数组的元素是按列进行存储的。也就是在一列上,元素的地址是连续的。
也就相当于将按行存储的数组逆转过来。其地址计算方式,和按行存储的相反。
a[i][j]=baseadress + (j*行数+i)*元素的长度。
这是Fortran和matlab等语言默认的存储方式。
二维数组是我刚学编程语言一直过不去的坎,知道后面学会了指针了解了数组的存储结构并查阅了大量资料才对其有了比较清晰的认识,怕以后忘记,因此写了一篇备忘录,以上都是作者本人对二维数组的理解,希望对大家学习有帮助。
如果有哪些错误之处,还有望各位大佬斧正!!!
本人邮箱:2930892897@qq.com