我们可以先看一个简单的例子,来说明一下二维数组的用法:
定义如下数组变量并初始化:
int a[3][3]={1,2,3,4,5,6,7,8,9}
定义一个能够指向二维数组的指针变量:(为什么能够指向二维数组)
int (*p)[3];
不看上面定义的数组名为a的二维数组,只看这个指针p,我们都知道这个指针变量p存储的是某个存储单元的首地址,那么这个存储单元有多大呢,这个指针变量写的是(*p)[3],很容易看出,这个存储单元能存储3个int类型的数据,也就是12个字节,既然p代表1个12个字节存储单的首地址,那么指针p+1与p之间也是相差12个字节的。
我们再来讨论一下数组,由上面声明定义可以看出数组a[0][0]的值是1,那么数组a[0][0]的址是&a[0][0],我们可以定义一个指针变量p1使其指向a[0][0]的首地址,int
*p1=&a[0][0],那么这个p1就是a[0][0](一个int类型存储单元)的首地址,那么我们知道,指针p1指向的内存单元是4个字节,即p1+1与p1相差4。
我们再来定一个一维数组int
b[3]={10,11,12};我们都知道,一维数组名b就是数组b的首地址,我们可以通过这个地址访问一维数组中的内容。那么二组数可以看成是一维数组的一维数组,a[3][3]可以看成是一维数组a[3]里面的每一个元素都是一个包含3个元素的一维数组a[3]。看下图:
a[2][2] |
a[2][1] |
a[2][0] |
a[1][2] |
a[1][1] |
a[1][0] |
a[0][2] |
a[0][1] |
a[0][0] |
既然可以把二维数组看成是一维数组的一维数组,那么a[0]可以看成是a[0][0]的首地址,其实C语言就是这么规划的,而a[0]所代表的存储单元像一维数组一样是一个int类型即4个节,那么a[0]+1,a[0]是地址,a[0]+1是向上偏移一个内存单元也就是4个字节,这个地址就就a[0][1]这个元素的首字节地址,这个已经清楚了。
我们再来看一来a代表什么,既然一维数组数组名是地址,那么二维数组的数组名是否是地址呢,我们用printf输出一下可以看出,输出a的内容和输出&a[0][0]的内容相等,自然也等于a[0],都是指向a[0][0]也是整个数组的首地址。既然a是地址那a指向的内存空间是多少呢,a[0]是a[0][0]的首地址,内存大小与a[0][0]存储大小有关即4个字节,那么我们可以把a看成是a[0]的首地址,C语言当中就是这么认为的。a是a[0]的首地址,那么a[0]所占内存大小是多少啊,从a[0][0]~a[0][2],3个int,也就是12个字节,那么我们就明白,a其实指向的是有12个字节内存单元的首地址,那么a+1与a相差12个字节。如果把这个二维数组看成一个表格,就是直接跳到第二行元素的首地址了。
我们已经知道p,a,a[0]这些地址表示内存单存的大小了,知道内存单元的大小就知道地址偏移1个单位,内存偏移多少个字节,从而可以找到我们最终放在二维数组中的元素。
那么我们怎么找到我们的元素呢,当然直接使用a[0][0]固然可以,比如我通过地址来找a[0][0],我们可以通过*a[0],首先a[0]存放的是a[0][0]的地址,并且指示的内存单元是4个字节,刚好是一个元素a[0][0]的存储空间,通过*指针运算符可以取到a[0][0]的内容,如果我们要找a[0][1]呢,由于数组在内存中是连续的,我们可以通地址偏移来获得,a[0]是a[0][0],而a[0][1]就是跟在a[0][0]后面的一个元素,可以通过a[0]+1向后偏移4个字节正好是a[0][1]的首地址,可以在这个地址中用*(a[0]+1)取出a[0][1]的内容。这个都明白,*(a[0]+4)等于a[1][0],而a[1]也是a[1][0]的首地址,也可能通过*a[1]来访问a[1][0],都是可以的。这是二维数组地址定义的特点。
那么我们能不能通a来访问a[0][0]的内容呢,当然可以,上面我们说过a也是a[0][0]的首地址,那么我们可以用**a访问a[0][0],这就奇怪了,为什么是**,大家也许会想,难道a内存放的是a[0][0]地址的地址?我们用printf("%d,%d,%d",a,a[0],&a[0][0]);输出的三个值都相等,说明这里的a就是a[0][0]的地址,不是它的间接地址,那么为什么要加两个*指针运算符呢,原因是,因为地址a
内存单存是12个字节也就是4个int,紧跟a的这个*其实是将a代表的12个字节的地址转成代表4个字节的地址,*a其实还是一个地址,也是a[0][0]的首地址,只是这个*a地址代表的内存范围变成4个字节了。远离a的这个*才是取这个地址单元中的内容,注意是4个字节的单元了。
那么同a一样(*p)[3],这个p也是指向12个字节的内存单元,*p也就是指向4个字节,为什么*p会被转成指向4个字节的单元呢,原因是我们在定义的时候就告诉编译器这个单元里面有三个元素每个元素占4个字节,这样就能处理了。
a是指向12个字节的内存单元,那a+1的地址就是12字节后,也就是3个int单元,也就是下一行,C语言弄出这么一个东西(a与a[0]不能大小内存单元)就是让我们能够很方便的访问行数据,使访问效率更高。
其实我们知道,int(*P)[3]就是二维数组的指针,我们可以将a 赋值
给p,即p=a。我们这样行不行,int
*p=a,把a这个地址给指针p行不行,这是不行的,编译会出错,a的内存是12个字节,而p指向的内存是4个字节,显然存不下。所以我们都把这个p也变成指向12个字节的首地址就可以了。
那么这样一个表达式:&a[0],&a这两个值是什么呢,注意:这里虽然有个取地址,其实&a[0]==a[0]==a==&a,这四个都相等,C语言内对这种运算进行了处理,对于数组和数组的指针,不对地址进行取地址(我是这么认为的),希望大家有什么更好的理解,或本文有什么错误可以指出。
加载中,请稍候......