数组与函数(C/C++)
一维数组
首先要明确一维数组的数组名并不是一个指针:
int a[] = {1,2,3,4};
int *p;
cout<<"数组名a的类型为\t"<<typeid(a).name()<<endl;
cout<<"指针p的类型为\t"<<typeid(p).name()<<endl;
p = a;
cout<<"赋值后指针p的类型为\t"<<typeid(p).name()<<endl;
输出结果:
数组名a的类型为 A4_i
指针p的类型为 Pi
赋值后指针p的类型为 Pi
由此可知:数组名不是指针,但是可以赋值给指针,赋值时数组名将隐式转换为指向数组首元素的指针。
所以这时sizeof(a) = 16,而sizeof§ = 4。只有在数组被定义的函数体内,数组名才具备这个作用,一旦数组名作为参数传入函数,它的类型就会变为指针,无论形参是如何定义的(int arr[]或者int *arr):
void type(int arr[])
{
cout<<"arr的类型为\t"<<typeid(arr).name()<<endl;
}
int main()
{
int a[] = {1,2,3,4};
cout<<"数组名a的类型为\t"<<typeid(a).name()<<endl;
type(a);
}
输出结果为:
数组名a的类型为 A4_i
arr的类型为 Pi
因此这时sizeof(arr)不再等于16而等于4,这也是为什么希望传入数组名的同时传入数组长度,因为在数组被定义的函数体内求数组长度是最方便的。
再有就是一个指针p可以用p[1]去表达*(p+1),这两种表述是等价的,另外对a取址将得到数组所对应的地址,这个地址等于数组首元素的地址,虽然数值相等,但含义是不一样的。
int a[] = {1,2,3,4};
cout<<&a<<endl; //数组地址
cout<<a<<endl; //这里实际上存在类型的隐式转换,数组首元素地址
输出结果:
0x61fe80
0x61fe80
总结:只有在被定义的函数体内数组名可保持原有类型,具备sizeof的作用,且取址操作等于数组地址。将数组传入函数时传入的是数组名与数组长度,此时的数组名就是指向数组首元素的指针。
二维数组
二维数组的数组名也不是指针!
int a[2][3] = {
{1,2,3},
{4,5,6}
};
cout<<"数组名a的类型为\t"<<typeid(a).name()<<endl;
cout<<"a[0]的类型为\t"<<typeid(a[0]).name()<<endl;
输出结果:
数组名a的类型为 A2_A3_i
a[0]的类型为 A3_i
由此可知:a是一个二维数组名,a[0]是一个一维数组名,所以对a[0]的操作与一维数组名一致。
sizeof(a) = 24, sizeof(a[0]) = 12, sizeof(a[0][0]) = 4。
对a取址得到的是二维数组的地址,对a[0]取址得到的是第一行一维数组对应的地址,对a[0][0]取址得到的是第一行第一列元素对应的地址,与直接输出a所得结果相等,但代表的含义是不同的。
int a[2][3] = {
{1,2,3},
{4,5,6}
};
cout<<"二维数组的地址为\t"<<&a<<endl;
cout<<"第一行的一维数组的地址为\t"<<&(a[0])<<endl;
cout<<"第一行第一列元素的地址为\t"<<&(a[0][0])<<endl;
cout<<"a = \t"<<a<<endl;
二维数组的地址为 0x61fe74
第一行的一维数组的地址为 0x61fe74
第一行第一列元素的地址为 0x61fe74
a = 0x61fe74
那么问题来了,a是否也被隐式转换成了指向数组首元素的指针?运行下面的代码:
int a[2][3] = {
{1,2,3},
{4,5,6}
};
int *p = a;
得到报错信息:error: cannot convert ‘int (*)[3]’ to ‘int*’ in initialization
int * p[3]; //p是一个数组,数组中有三个int型指针
int (*p) [3]; //p是一个指针,指向一个int[3]数组
意思很明显,不能将一个指向int[3]数组的指针值赋给一个指向int型的指针,也就是说a被隐式转换成了一个指向int[3]数组的指针,即指向二维数组第一行一维数组的指针,这等价于&(a[0]),所以正确的写法应该是:
int (*p)[3] = a; //将p也定义为一个指向int[3]数组的指针,这里的a会被隐式转换为指向a[0]的指针,
//a[0][0]=**p,第一次解引用得到a[0]数组的地址(也是a[0][0]的地址),第二次解引用才拿到a[0][0]的
//值,所以p类似于一个二级指针,a的隐式转换也类似于一个二级指针
// 或者
int *p = a[0]; //这里利用了a[0]会被隐式转换为指向a[0][0]的指针
// 或者
int p[][3] = a; //将p定义为数组名,无隐式转换
所以,同样的,在将二维数组传入函数时,最后同时传入其行数与列数,因为在被定义的函数体内求这两个值最方便。
二维数组传入函数的方法:
void f(int (*p)[列数],行数,列数); //f(a,行数,列数)
void f(int *p,行数,列数); //f(a[0],行数,列数)
void f(int p[][列数],行数,列数); //f(a,行数,列数)
其中第二种方法最常用也最简单,因为在定义函数时无需事先知道数组的列数,相当于将二维数组当一维数组处理。
总结:a会被隐式转换为指向a[0]的指针,a[0]会被隐式转换为指向a[0][0]的指针,降一维,a = &(a[0])