事出缘由
char a[3][3];
sizeof(a); //结果是9
9这个结果一直认为是“理所当然”,但是当自己实现一个二维数组时,最简单的方式就是:
char **test = (char**)malloc(sizeof(char*)*ROW_SIZE);
int i=0;
for(;i<ROW_SIZE;++i)
test[i]=(char*)malloc(sizeof(char)*COL_SIZE);
其实这样获得数组大小是ROW_SIZE*COL_SIZE+ROW_SIZE*4字节没错吧。
上述内存并非连续,那连续的
可是还是得有前面的几个一维指针指向后面的一位数组,所以大小还是上面那个……
所以我的就有些惊奇,写了测试,结果打印出来的内容更加让我惊奇……char a[3][3]得到的&a==&a[0]==&a[0][0]。但是细细想来,这与sizeof(a)==9没有一点矛盾冲突。可是问题又来了,C语言在没有重载[]的情况下是如何实现的(代码中我是用c++写的)?是编译器的功劳么?
测试代码
#include <iostream>
using namespace std;
int main()
{
char **x=(char**)malloc(sizeof(char*)*5);
for(int i=0;i<5;++i)
x[i]=(char*)malloc(sizeof(char)*3);
char a[3][3];
char b[9];
/*
* 无所谓的初始化
*/
for(int i=0;i<3;++i)
for(int j=0;j<3;++j)
a[i][j] = '0'+i+j;
cout<<hex<<"输出a的地址(&a):"<<endl
<<(unsigned long)&a<<""<<endl
<<"输出a+i的内容(应为a[i]的地址):"<<endl
<<"a:"<<(unsigned long)a<<"\t\t"
<<"a+1:"<<(unsigned long)(a+1)<<"\t"
<<"a+2:"<<(unsigned long)(a+2)<<endl
<<"输出a[i]的地址:"<<endl
<<"&a[0]:"<<(unsigned long)&(a[0])<<'\t'
<<"&a[1]:"<<(unsigned long)&(a[1])<<'\t'
<<"&a[2]:"<<(unsigned long)&(a[2])<<endl
<<"输出a[i]的内容(应为a[i][0]的地址):"<<endl
<<"a[0]:"<<(unsigned long)a[0]<<'\t'
<<"a[1]:"<<(unsigned long)a[1]<<'\t'
<<"a[2]:"<<(unsigned long)a[2]<<endl
<<"输出a[i][0]的地址:"<<endl
<<"&a[0][0]:"<<(unsigned long)&a[0][0]<<'\t'
<<"&a[1][1]:"<<(unsigned long)&a[1][1]<<'\t'
<<"&a[2][2]:"<<(unsigned long)&a[2][2]<<endl;
cout<<endl<<"所有元素(内容)的地址"<<endl;
for(int i=0;i<3;++i){
for(int j=0;j<3;++j)
cout<<hex<<"&a["<<i<<"]["<<j<<"]:"<<(unsigned long)&a[i][j]<<' ';
cout<<endl;
}
cout<<"Sizeof(a[3][3]):"<<sizeof(a)<<endl;
cout<<endl<<"输出b的地址(&b):"<<endl<<(unsigned long)&b<<endl
<<"输出b的内容(应为b[0]的地址):"<<(unsigned long)b<<endl
<<"输出b[0]的地址:"<<(unsigned long)&b[0]<<endl;
return 0;
}
测试结果
问题思考
其实这个问题就是数组在内存中的存放形式以及编译器是如何处理数组的初始化的。于是,我就取巧地在优快云发了贴询问,原帖内容。我决定在这里记一笔,整理一下比较好。
对于一维数组:
int a[3];
&a[i]=(int*)a+i;
对于二维数组:
int a[3][3];
&a[i]=(int(*)[5])a+i
归纳总结一下:
template <typename T, size_t N>
设有定义
T a[N];
那么对于任意int i有
a[i] == *((T*)a + i)
这是数组的寻址方式
特别内容
这里要特别强调一下,一直以来都把指针变量和数组名等价,但从&a==a这一点可以看出,数组名并不是指针变量,它不存在自己的内存空间,只是一个符号而已。见下:
#include <iostream>
using namespace std;
int main(){
char a[3][3];
typedef char Z[3][3];
typedef char (*X)[3];
Z &c = a;
X d = a;
cout<<hex<<(unsigned long)c<<endl
<<(unsigned long)d<<endl
<<(unsigned long)a<<endl
<<(unsigned long)&c<<endl
<<(unsigned long)&a<<endl
<<(unsigned long)&d<<endl;
return 0;
}
从上面的代码中可以看出来,引用的作用和数组名的作用是相同的,然而指针则是不一样的!!!!
结论
所以说当a[i][j]进行寻址时,编译器在编译时直接就将其转化为上述形式,无需其他额外地址进行辅助。然而人为地创造一个数组,并且对这一块连续区域进行操作,就必须自己设定一种方法——额外添加头指针用来寻址。