名正在想的博客

二维数组、二维指针及其作为参数的深入理解

By 名正在想


引言

这篇文章探讨的内容是由李炅同学和我的讨论产生的,在此之前我对一些问题也不甚了了,多方查阅资料后,我在此做出总结,与大家共同学习。

首先,我们从两个简单的问题开始。

(注:本文详细叙述了探究过程,若嫌太长可直接看总结部分)

两个问题

一维数组的数组名是指针吗?

你可能会说:是的(实际上某些书中甚至也是这么说的)。众所周知,如果我们使用如下语句:

int a[3];
cout << a; //这里使用了C++的输出语句,相信大家可以望文知义

输出结果是一个地址,而且,如果我们定义一个指针p,并执行p=a,也可以正常执行。这不都说明了数组名a本身就是一个指针吗?

然而,事实并非这么简单,让我们使用以下语句查看a与p的数据类型。

int a[3];
int* p;
cout << "type of a is: " << typeid(a).name() << endl;
cout << "type of p is: " << typeid(p).name() << endl;

得到的输出结果是:

type of a is: A3_i
type of p is: Pi

上述结果含义为:

a的类型为 int [3] ,是一个有3个元素的int型数组 p的类型为 int* ,是一个指向int型变量的指针

既然类型不同,为什么可以进行p=a的赋值操作呢?这是因为, int [N] 可以经过类型转换,变为 int* 类型。当我们执行类似a+0的操作(获取数组某个元素的地址)时,实际上就进行了隐式类型转换将其转换为指针,我们可以进行如下测试:

cout << "type of a+0 is: " << typeid(a+0).name() << endl;

得到结果:

type of a+0 is: Pi

综上,数组名并非指针类型,但可以被转换为指针。

一维数组可以作为参数传入函数吗?

答案似乎又是显而易见的:可以!因为我们很早之前就学过数组作为参数。为了验证这一点,我们不妨尝试如下程序:

void output_size(int a[]){
    cout << "---output_size---" << endl << sizeof(a) << endl;
    return;
}
int main() {
    int a[3];
    cout << "---main---" << endl << sizeof(a) << endl;
    output_size(m);
    return 0;
}

输出结果:

---main---
12
---output_size---
4

这真是匪夷所思!好端端的一个数组,为什么传参之后大小就变了呢?原来,当数组作为参数时,就“丧失了本心”,被转换为一个指针,因此在output_size()函数中sizeof(a)返回的实际上是一个指针的长度,而不是数组的长度。

细心的读者可能会发现问题:在sizeof(a)中,a也是作为sizeof()函数的一个参数,为什么没有被转换为指针呢?实际上,这是一个仅有的特例,因此,我们可以进行如下总结:

当数组作为参数时(作为sizeof()的参数除外),会自动被转换为指针。

二维指针与二维数组

未完待续……

总结

  • 数组名并非指针类型,但可以被转换为指针。
  • 当数组作为参数时(作为sizeof()的参数除外),会自动被转换为指针。

对于如下定义:

int a[3][4];
表达式 类型 类型解释 备注
a int [3][4] 二维数组,3行4列 可被转换为行指针
a+0 int (*)[4] 行指针(数组指针) 指向一个有4列的数组
*(a+0) int [4] 数组,4列 可被转换为列指针,等价于a[0]
*(a+0)+0 int * 列指针 指向一个元素
*(*(a+0)+0) int   等价于a[0][0]

未完待续……