有的实际问题一维数组是无法解决的,所以引入二维数组甚至是多维数组,这里着重讨论一下二维数组,如果深刻理解二维数组多维数组也就迎刃而解。
1. 二维数组的定义:例如现在有3支球队,每支队有5个人,要充分表达每个人便可以引入二维数组,例如a[0][0]就表示第一支队伍的第一个人。这里的a便是数组名,第一个[0]表示第一支队伍,第二个[0]表示第一支队伍里的第一个人。根据此类比喻可以根据条件要求将二维数组扩展为多维数组。
2. 二维数组在内存中布局:我们可以把二维数组的布局想象成表格,第一行表格便是第一行一维数组,但是事实上二维数组在内存中的布局并不是这样的,因为内存不是表状的,而是线形状的。相当于一把尺子,变量在内存中开辟的值便从尺子的高地址处依次向第地址出排布的,那么二维数组的存储也符合这样的规则,从较高地址处排布a[0][0]直到较低地址处排布a[3][5],直到整个数组存储完毕。二维数组在内存中存储遵循最右边下标率先变化的规则,例如int *p; p=a[0][0]; ++p; 那么++p是谁的地址呢?根据上述规则的++p应该是*a[0][1]。对于[][]里面的下标可以认为第一个为行,也可以认为它表示列,这些没有严格的限制,只要遵循上下文的逻辑意思,同时不改变它的实际存储方式即可。
3. 二维数组名:首先我们知道数组名就是数组首元素的地址,所以a便是&a[0],又因为a[0]是第一行的一维数组,它也是一个数组名,它也代表该一维数组首元素的地址,a[0]=&(一维数组第一个元素)。所以说,可以把二维数组理解为一个二级指针便不难理解。a+1便让二维数组偏移到了第二行的以为数组,它等同于(&a[0]+1),(&a[0]+1)表示是指向第二行一维数组的指针,所以*(&a[0]+1)便的到第二行一维数组的名字,即*(a+1)也是第二行一维数组的名字,即a[1],*(*(a+1)+1)便是a[1][1]的值。
4. 二维数组的初始化:二维数组初始化有两种方式,第一种是给出长长的初始值列表例如int a[2][2]={1,2,3,4};第二种方式是int a[2][2]={1,2},使用包含2个复杂元素的一维数组。
5. 指向二维数组的指针,如果有int a[5]; int *p=a,这是正确的,p是一个指向整型的指针,并且把它初始化指向数组a的第一个元素。对于二维数组int a[2][2]; int *p=a,是不对的,因为p是一个指向整型的变量,它并没有能力指向一个数组,所以应该这样写:int a[2][2]; int (*p)[2]=a; p是一个指向拥有2个整型元素的数组的指针,也就是一个数组指针变量,初始化为a。
6. 函数参数中二维数组用法:和一维数组相同,二维数组名实际传过去的是指向数组第一个元素的指针,因为二维数组中每个元素又是一个一维数组,所以编译器需要知道它的位数。在一维数组中我们知道,函数的形参原型有两种书写方式,例如:void(int *p)或void(int p[])。这里我们可以进行模仿 例如:void(int (*p)[2])或者void(int p[][2]),第一个表示一个指针数组,它有能力指向有两个元素的一维数组,所以这样接收实参不会有问题;第二种表示第二种是一种数组的形式接收形参,其实和一维数组同理,它还是接收的是数组的地址。这两种方式都同时要注意第一维的写法可以变化,但是第二维的数字绝对都能省略,因为只有知道它才能对各下标进行求值运算。
对于二维数组在函数中的实参:实参直接用数组名即可