要理解本节内容,必须掌握在一维数组时候学到的几个概念:

例子:

int buf[5];
  1. buf作为指针时,代表数组第0个元素(变量)的第一个字节的地址,等价于&buf[0]
  2. 因为buf可以代表第0个元素的指针,所以*buf就是数组第0个元素解引用
  3. &buf是数组指针

一、二维数组的定义形式

int buf[m][n];

比如:

int buf[3][4];
buf[0][0] buf[0][1] buf[0][2] buf[0][3]   第0个小一维数组

buf[1][0] buf[1][1] buf[1][2] buf[1][3]   第1个小一维数组

buf[2][0] buf[2][1] buf[2][2] buf[2][3]   第2个小一维数组

二维数组由3个小一维数组来组成的,每个小一维数组4个元素


二、二维数组的内存结构

int buf[3][4]; //没有初始化

内存结构以这个为例:
“二维数组”的存储空间是一片连续的空间,只是这片空间被划分为了3个小的一维数组,每个一维数组又有4个元素

在未初始化时,每个元素内容为多少,视数组所在内存位置而定,

  • 如果是全局数组,或者是静态局部变量,在静态数据段,默认就是为0
  • 如果是自动局部变量数组,在桟里面就是随机值;

三、初始化

1. 给定空间的初始化

全部初始化为0

int a[2][2] = {};

注意:

int a[2][2] = {0}; // 第一个元素为0,其它默认也为0
int a[2][2] = {1}; // 第一个元素为1,其它默认为0

2. 完全初始化

int a[2][2] = {0, 1, 2, 3};

更规范的写法应该为

int a[2][2] = {{0, 1}, {2, 3}};

01用于初始化0一个小一维数组的两个元素,
23用于初始化第1个小一维数组的两个元素

3. 部分初始化

int a[2][2] = {0, 1, 2}; // 或 = {{0, 1}, {2}},没被初始化的其它元素的值为0

int a[2][2] = {, 1,, 2,}写法是不对的

4. 个别初始化

int a[2][2] = {[1][0]=10, [0][1]=9, [1][1]=3}; // 未初始化的其它元素默认为0

5. 不给定空间的初始化

int a[][2]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

实际上我们仍然是给定了空间的,

行 = 元素个数/列

如果不能被整除的话,那就补增元素,直到能被整除为止,补增元素的值默认为0

提问:int a[2][]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 写法是否可以?

编译器并不支持这种写法。


四、赋值

与一维数组一样,不能够整体赋值,只能单个元素赋值,或者使用memcpy来实现赋值

1. 单个元素赋值

#include <stdio.h>

int a[3][3];

int main(void)
{       
    int i = 0, j = 0;

    for(i=0; i<3; i++)
    {
        for(j=0; j<3; j++)
        {
            a[i][j] = xxxx;
            printf("^ %d\n", a[i][j]);
        }
    }

    return 0;   
}

2. memcpy 函数

int buf1[2][2] = {{0, 1}, {2, 3}};
int buf2[2][2];

memcpy((void *)buf2, (void *)buf1, sizeof(buf1));

五、一维、二维数组的关系

int a[3][4]

以上为例:二维数组其实还是一个一维数组,我们可以从两个角度来看这个问题:
(1)第一个角度

二维数组就一个只有三个元素的“一维数组”,只是每个元素又是一个小的“一维数组”。

(2)第二个角度

忽略每个小的一维数组,我们完全可以将整个二维素组看成是一个大号的有12个元素一维数组

六、二维数组的“数组名”

int buf[3][4];

1. 代表整个数组

buf的含义:数组的名字,代表整个数组空间,sizeof(buf),此时buf就是这个含义

此时buf代表的这个二维数组就是一个大号的变量,sizeof(buf)所得到的就是这个大号变量的空间大小

2. 代表第 0 个元素的指针

由于二维数组的第0个元素是一个“一维数组”,所以buf就是第0个一维数组的“数组指针”,
也就是“第0个一维数组”的“第一个字节的地址”,等价于&buf[0]

3. buf 的指针类型

每个“小一维数组”为4个元素的int数组,所以buf的数组指针类型为int (*)[4]

由于buf是第0个一维数组的数组指针,而buf+1加的是一个“小一维数组空间”的大小,小一维数组为4个元素,
所以 buf+1 等于 buf+1*4*sizeof(int),得到的就是第1个一维数组的“数组指针”

buf+i:通过 “第0一维数组的数组指针” + i,可以跳转到任何一个小一维数组的第一个字节处,
然后再将“数组指针”转为第0个元素的指针后,就可以访问“小一维数组”中的每一个元数了

4. &buf

根据:&第0个元素的指针 ————————> 数组的数组指针

buf为二维数组第0个元素的指针,所以&buf为整个二维数组的“数组指针

二维数组的数组指针的指针类型为int (*)[3][4]

5. *buf

*buf的含义

buf为二维数组第0个元素的指针,也就是第0个“一维数组”的“数组指针”,
根据 *数组指针 ————————> 数组第一个元素的指针

所以*buf为“第0个一维数组”的第0个元素的指针


*buf 的指针类型

小一维数组的每个元素为int的普通变量,那么*buf的指针类型就为int *
*buf等价于*(buf+0)


**buf:

**buf ———> *(*(buf+0)+0) ———> buf[0][0]
*(*(第0个一维数组的数组指针)+0)————>

*(第0个一维数组的第0个元素的指针 + 0) ——> 

第0个一维数组的第0个元素的空间
**buf 其实就是buf[0][0]

按照[]与”指针访问方式”之间的等价关系,也可以得出这个结论。

*(*(buf+0)+0) ————>*(buf[0]+0) ————————>buf[0][0]

*(*(buf+i)+j):第i个数组中第j个元素的空间

根据我们前面所讲的内容,

*(*(buf+i)+j) ——> *(buf[i]+j) ——> buf[i][j]

这几个写法都是成立的,(*(buf+i))[j]的写法也是正确的

七、二维数组的buf[0]、buf[1]等的含义

在面试的时候,可能会见到询问二维数组中buf[0]buf[1]含义的题目,很多人都想记住它们的含义,
但是实际上是记不住的,但是只要掌握了分析方法,我们可以很快搞清楚它的含义,以后分析3维等多维数组时也是相同的规则

例子:

int buf[3][2];

buf[0][0]  buf[0][1]
buf[1][0]  buf[1][1]
buf[2][0]  buf[2][1]

buf[0]buf[1]的含义

回想一维数组,我们通常都是通过数组名[]的形式访问的元素,“数组名”又代表了数组的第0个元素指针
所以我们可以理解为数组名[i],其实就是数组第0个元素地址[i]的方式来访问数组元素

这里的buf[0]就是二维数组的第0个元素,因为二维数组的第0个元素是一个数组,
且与一维数组的“数组名”(数组名就是第0个元素的指针)同理,所以buf[0]就代表了第0个一维数组的第0个元素

buf[0]buf[1]buf[2]的指针类型为int *


buf[0]是二维数组第0个一维数组的第0个元素的指针,

根据:&第0个元素的指针 ——————> 数组指针,所以&buf[0]为第0个一维数组的数组指针

我们前面讲过,&buf[0]等价于buf


buf[0]为第0个一维数组的第0个元素的指针,通过*解引用后,
*buf[0]代表的就是第0个一维数组的第0个元素的空间,其实就是buf[0][0]元素的空间