C语言多维数组

多维数组

如果某个数组的维数超过1,它就被称为多维数组,例如,下面这个声明:

int matrix[6][10]

创建了一个包含60个元素的矩阵。但是,它是6行每行10个元素,还是10行每行6个元素?
为了回答这个问题,你需要从一个不同的视点观察多维数组。考虑下列这些维数不断增加的声明:

int a;
int b[10];
int c[6][10];
int d[3][6][10];

a是个简单的整数,接下来的那个声明增加一个维数,所以b就是一个向量,它包含10个整形元素。

c只是在b的基础上再增加一维,所以我们可以把c看做是一个包含6个元素的向量,只不过它的每个元素本身是一个包含10个整形元素的向量。换句话说,c是个一维数组的一维数组。d也是如此,它是一个包含三个元素的数组,每个元素都是包含6个元素的数组,而这6个元素中的每一个都是包含10个整形元素的数组,间接地说,d是一个3排6行10列的整形三维数组。

·

数组名

一维数组名的值是一个指针常量,它指向数组的第一个元素,它的类型是“指向元素类型的指针” 。多维数组也差不多简单,唯一的区别是多维数组第1维的元素实际上是另一个数组。例如,下面这个声明:

int matrix[3][10];

创建了matrix,它可以看做是一个一维数组,包含3个元素,只是每个元素恰好是包含10个整形元素的数组。
matrix这个名字的值是一个指向它第一个元素的指针,所以matrix是一个指向一个包含10个整型元素的数组的指针。

下标

如果要标识一个多维数组的某个元素,必须按照与数组声明时相同的顺序为每一维都提供一个下标,而且每个下标都单独位于一对方括号内。在下面的声明中:

int matrix[3][10];

表达式matrix[1][5]访问下面这个元素
在这里插入图片描述

但是,下标引用实际上只是间接访问表达式的一种伪装形式,即使在多维数组中也是如此,考虑下面这个表达式:

matrix

它的类型是“指向包含10个整型元素数组的指针”,它的值是
在这里插入图片描述
表达式 matrix+1也是一个“指向10个整型元素数组的指针”,但它指向matrix的另一行
在这里插入图片描述
为什么?因为1这个数值根据包含10个整型元素的数组的长度进行调制,所以它指向matrix下一行,如果对其执行间接访问,就如下图随箭头选择中间的这个子数组
在这里插入图片描述
所以表达式*(matrix+1)事实上标识了一个10个整型元素的子数组。数组名的值是个常量指针,它指向数组的第一个元素,在这个表达式中也是如此。它的类型是“指向整型的指针”,我们现在可以在下一维的上下文环境中显示它的值。
在这里插入图片描述
现在请拿稳你的帽子,猜猜下面这个表达式的结果是什么?

*(*(matrix+1)+5)

它所访问的正是那个整型元素。如果它作为右值使用,你取得存储于那个位置的值,如果它作为左值使用,这个位置将存储一个新值。
这个看上去吓人的表达式实际上正是我们的老朋友–下标,我们可以把表达式*(matrix+1)改写成matrix[1],把这个下标表达式带入原先的表达式,我们将得到:

*(matrix[1]+1);

这个表达式完全合法的,matrix[1]选定一个子数组,所以它的类型是一个指向整型的指针,我们对这个指针加上5,然后执行间接访问操作。
但是,我们可以再次用下标代替间接访问,所以这个表达式还可以写出:

matrix[1][5]

指向数组的指针

下面这些声明合法吗?

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

第一个声明是合法的。它为一个整型数组分配内存,并把vp声明一个指向整型的指针,并把它初始化为指向vector数组的第一个元素。vector和vp具有相同的类型:指向整型的指针。但是第2个是非法的。它正确地创建了matrix数组,并把mp声明为一个指向整型的指针。但是mp的初始化是不正确的,因为matrix并不是一个指向整型的指针,而是一个指向整型数组的指针。我们应该怎样声明一个指向整型数组的指针呢?

int (*p)[10];

下标引用的优先级高于间接访问,但由于括号的存在,首先执行的还是间接访问。所以p是个指针,但它指向什么呢?
接下来执行的是下标引用,所以p指向某种类型的数组。这个声明表达式中并没有更多的操作符,所以数组的每个元素都是整数。
声明并没有直接告诉你p是什么,但推断它的类型并不困难,当我们对它执行间接访问操作时,我们得到的是个数组,对该数组进行下标引用操作得到的是一个整型值。所以p是一个指向整型数组的指针

在声明中加上初始化后是下面这个样子:

int (*p)[10] = matrix;

它使p指向matrix的第一行。

作为函数参数的多维数组

作为函数参数的多维数组名的传递方式和一位数组名相同,实际传递的是个指向数组第一个元素的指针。但是,两者之间的区别在于,多维数组的每个元素本身是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式进行求值。这里有两个例子,说明了它们之间的区别:

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

参数vector得我类型是指向整型的指针,所以func1的原型可以是下面两种的任何一种:

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

作用于vec上面的指针运算把整型的长度作为它的调整因子。
现在让我们来观察一个矩阵:

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

这里matrix的类型是指向包含10个整型元素数组的指针。func2的原型应该是怎样的呢?你可以使用下面两种形式中的任何一种:

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

在这个函数中,mat的第一个下标根据包含10个元素的整型数组的长度进行调整,接着第2个下标根据整型的长度进行调整,这和原先的matrix数组一样。

在编写一维数组形参的函数原型时,你既可以把它写成数组的形式,也可以把它写成指针的形式。但是,对于多维数组,只有第1维可以进行如此选择。尤其是,把func2写成下面这样的原型是不正确的:

void func2(int**mat);

这个例子把mat声明为一个指向整型指针的指针,它和指向数组的指针并不是一回事。

指针数组

除了类型之外,指针变量和其他变量很相似,正如你可以创建整型数组一样,你也可以声明指针数组。这里有一个例子:

int *api[10];

为了弄清楚这个复杂的声明,我们假设它是一个表达式,并对它进行求值。

下标引用的优先级高于间接访问,所以在这个表达式中,首先执行下标引用。因此,api是某种类型的数组,元素个数为10。在取得一个数组元素之后,随机执行的是间接访问操作,这个表达式不再有其他操作,所以它的结果是一个整型值。
那么api到底是什么东西?对数组的某个元素执行间接访问操作后,我们得到一个整型值,所以api肯定是个数组,它的元素类型是指向整型的指针。

小结

一维数组的数组名指向第一个元素,类型是指向元素类型的指针。
二维数组的数组名是也指向它第一个元素,类型是指向数组的指针。
指针的指针是指向某种类型指针的指针,它和指向数组的指针并不是一回事。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值