存储顺序

在C语言中,多维数组的元素存储顺序是按照最右边的下标率先变化的原则,称为行主序。例如:a[3][6]

多维数组_初始化

元素的下标值分别为:0,0  0,1 0,2 0,3 0,4 0,5 1,0 1,1 1,2 1,3 1,4 1,5 2,0 2,1 2,2 2,3 2,4 2,5

数组名

一维数组名的值是一个指针常量,它的类型是“指向元素类型的指针”,它指向数组的第一个元素,多维数组也差不多,唯一的区别是多维数组第1维的元素实际上是另一个数组。

如:

int matrix[3][10]

 可以看作是一个一维数组,包含3个元素,每隔元素恰好是包含10个整型元素的数组。

下标

下标引用实际上只是间接访问表达式的一种伪装形式,上例中

matrix:它的类型是指向包含10个整型元素的数组的指针,它的值是:

多维数组_初值_02

它指向包含10个整型元素的第1个子数组。

表达式:matrix+1,也是指向包含10个整型元素的数组的指针,它指向matrix的下一行:

多维数组_多维数组_03

表达式:*(matrix+1)标识了一个包含10个整型元素的子数组。数组名的值就是个常量指针,它指向数组的第1个元素,它的类型是指向整型的指针,即:

多维数组_多维数组_04

表达式:*(matrix+1)+ 5,前一个表达式是指向整型值的指针,所以5这个值根据整型的长度进行调整,整个表达式的结果是一个指针,它指向的位置比原先的位置向后移动了5个整型元素。即:

多维数组_初值_05

对其执行间接访问操作:*(*(matrix+1)+ 5),它所访问的就是图中哪个整个整型元素。

我们可以把表达式*(matrix+1)改写为matrix[1],matrix[1]选定一个子数组,所以它的类型是一个指向整型的指针。而对于上面的表达式*(*(matrix+1)+ 5)可以写成:*(matrix[1]+5),当然也可以直接写成matrix[1][5]。

指向数组的指针

下面的定义: 

int vector[10],*vp=vector;
int matrix[3][10],*mp=matrix;

第一个声明是合法的,它为一个整型数组分配内存,并把vp声明为一个指向整型的指针,并把它初始化为指向vector数组的第一个元素,vector和vp具有相同的类型:指向整型的指针。

第二个声明是违法的,它创建了matrix 数组,并把mp声明为一个指向整型的指针,但是mp的初始化是不正确的,因为matrix并不是一个指向整型的指针,而是一个指向整型数组的指针,应该声明为:

int (*p)[10]matrix;

下面的形式:

int(*p)[]=matrix;

若是想在指针上进行任何的指针操作,应该避免这种形式的声明,p仍然是一个指向整型数组的指针,但是数组的长度未知,当某个整数与这种类型的指针进行计算时,它的值将根据空数组的长度进行调整。有可能导致错误。

作为参数的多维数组

作为函数参数的多维数组名的传递方式和一维数组名相同,实际传递的是指向数组第1个元素的指针,但是两种的区别在于,多维数组的每个元素本身是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式求值:

对于一维数组

int vector[10];
...
func1(vector);

 参数vector的类型是指向整型的指针,所以func1的原型可以是:

void func1(int* vec);
void func1(int vec[]);

作用于vec上的指针运算把整型的长度作为调整长度。

对于矩阵:

int matrix[3][10];
...
func2(matrix);

这里,参数matrix的类型是指向包含10个整型元素的数组的指针,func2的原型定义形式:

void  func2(int (*mat)[10]);
void  func2(int mat[][]);

这里的关键在于编译器必须知道第2个及以后的各维的长度才能对下标求值,因此原型声明中必须给出这些维度,第一维的长度并不重要,因为计算下标时用不到它。把原型定义成:

void func2(int** mat);

是不正确的,因为mat在这里表示指向整型指针的指针,它和指向数组的指针并不是一回事。

初始化

以二维数组为例:

初始化形式为;
数据类型 数组名[整常量表达式][ 整常量表达式]={ 初始化数据 };
在{ }中给出各数组元素的初值,各初值之间用逗号分开。把{ }中的初值依次赋给各数组元素。

分行进行初始化: 

int a[2][3]={{1,2,3},{4,5,6}};

 在{ }内部再用{ }把各行分开,第一对{ }中的初值1,2,3是0行的3个元素的初值。第二对{ }中的初值4,5,6是1行的3个元素的初值。

不分行的初始化:

int a[2][3]={ 1,2,,4,5,6};

把{ }中的数据依次赋给a数组各元素(按行赋值)。

为部分数组元素初始化:

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

第一行只有2个初值,按顺序分别赋给a[0][0]和a[0][1];第二行的初值4赋给a[1][0]。

省略第一维:

int a[ ][3]={ 1,2,3,4,5,6};

可以省略第一维的定义,但不能省略第二维的定义。系统根据初始化的数据个数和第2维的长度可以确定第一维的长度。

 a数组的第一维的定义被省略,初始化数据共6个,第二维的长度为3,即每行3个数,所以a数组的第一维是2。

一般,省略第一维的定义时,第一维的大小按如下规则确定:
初值个数能被第二维整除,所得的商就是第一维的大小;若不能整除,则第一维的大小为商再加1。例如:

int a[ ][3]={ 1,2,3,4}

等价于:

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

若分行初始化,也可以省略第一维的定义。下列的数组定义中有两对{ },已经表示a数组有两行。

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