数组的内存模型

1 一维数组的内存模型

一维数组说开了就是一组连续的数值,这组数值存储在一段连续的内存空间之中,只是不一样的是,若是我们不定义数组我们来访问这一连串的数值需要n个变量对应这n个数值或是需要一个指针进行遍历。只是现在我们为这一组值起了个名字而已,以后我们便可以通过这个名字+下标来访问这一组值了。

如我们定义了一个数组:

char a[] = “hello world”;

它的大小为sizeof a = 12(包含一个’/0’)

如果我们不想包含这个’/0’,可以改用下面的方式初始化:

char a[] = {'h','e','l','l','o','w','o','r','l','d'};

这样数组的大小就变成了sizeof a = 10;去掉了一个空格和’/0’;

下面我们用程序探索一下它的内存模型:

#include "stdio.h"

void main()

{

    char a[] = {'h','e','l','l','o','w','o','r','l','d'};

    printf("addr of &a:%#x/n",&a);

    printf("addr of a:%#x/n",a);

    printf("addr of a[0]:%#x/n",&a[0]);

    printf("addr of a[1]:%#x/n",&a[1]);

    printf("addr of &a+1:%#x/n",&a+1);

    printf("addr of a+1:%#x/n",a+1);

}

输出结果为:

 

好的,我们来分析一下结果:

在分析之前我们先去理解一些这几个符号所代表的含义:

&a: -       代表整个数组的地址

a:          代表第一个元素的地址

&a[0]:            代表第一个元素的地址

&a[1]:            代表第二个元素的地址

&a+1:            最末端后一个地址

a+1:               第一个元素的地址

&a[0]+1:        第一个元素的地址

如果你能把这些都理解了,那也就无需分析了。呵呵。。。

好的,我们来看一下它的内存模型吧:

 

2 二维数组

二维数组比一维数组稍复杂了些,它可以理解为数组的数组,即在一维数组里每个元素又是一个数组。

同样我们用一个程序来看一下二维数组的内存模型:

#include "stdio.h"

char a[2][5];

void main()

{

    printf("addr of &a:%#x/n",&a);

    printf("addr of a:%#x/n",a);

    printf("addr of a[0]:%#x/n",&a[0]);

    printf("addr of a[1]:%#x/n",&a[1]);

    printf("addr of &a+1:%#x/n",&a+1);

    printf("addr of a+1:%#x/n",a+1);

    printf("addr of a[0][0]:%#x/n",&a[0][0]);

    printf("addr of a[0][1]:%#x/n",&a[0][1]);

    printf("addr of a[1][0]:%#x/n",&a[1][0]);

}

输出结果:

 

用图来表示内存模型如下:

 

3 三维数组

三维数组更为复杂,在我们编程生活中很少遇得到的。不过我们还是应该了解一下的.

同样程序:

#include "stdio.h"

int apricot[2][3][5];

int (*r)[5] = apricot[0];

int *t = apricot[0][0];

void main()

{

    printf("addr of apricot:%#x/n",apricot);

    printf("addr of r:%#x/n",r);

    printf("addr of t:%#x/n",t);

    printf("addr of ++r:%#x/n",++r);

    printf("addr of ++t:%#x/n",++t);

    printf("addr of apricot+1:%#x/n",apricot+1);

    printf("addr of &apricot+1:%#x/n",&apricot+1);

    printf("addr of apricot[0][0][0]:%#x/n",&apricot[0][0][0]);

    printf("addr of apricot[0][0][1]:%#x/n",&apricot[0][0][1]);

    printf("addr of apricot[1][0][0]:%#x/n",&apricot[1][0][0]);

}

输出结果:

 

由于三位数组比较多,这里就不在画图了,它在内存中的存储仍是顺序存储的。即如:

char  a[2][3][5];这个三位数组.它有两个元素,每个元素又是一个二维数组。存储的时候首先存储第一个二维数组,然后在顺序存储第二个二维数组。

总结:

经过对一位数组,二维数组,三维数组的分析,我们可以总结出一个规律。即当一个地址+1的时候,这个1的含义是根据这个地址的类型来确定的。如:

int a[5];

a代表第一个元素的地址。它的类型为一个元素的地址。故a+1代表a[1].只往后移动了一个元素位置,即sizeof(*a);

&a代表整个数组的地址。所以&a+1.往后移动的长度为:sizeof(a);

&a[1]代表第一个元素的地址。所以&a[1]+1往后移动的长度为:sizeof(a[1]);

:

int a[3][10];

a  代表第一维的地址。所有a+1往后移动的距离为:sizeof(a[0]);:10*sizeof(int)

&a 代表二维数组的地址.所以&a+1往后移动过的距离为:sizeof(a);3*10*sizeof(int);

&a[1]代表第二维的地址。所以&a[1]+1往后移动的距离为:sizeof(a[1])+1:10*sizeof(int).

另外我们总结一下能够遍历数组所需用的指针:

遍历一位数组:

:int a[10];

由于数组中的元素的类型为int,所以我们只需一个int型指针就可以遍历数组了:

int *p = a;

遍历二维数组:

:int a[3][20];

由于我们可以理解为是数组的数组。所以数组元素的类型为数组。所以我们需要一个数组指针来遍历二维数组:

int (*p)[20];

p = a;

现在p为指向含有20个元素的一位数组指针。他可以指向a[0],a[1],a[2].如需要再去遍历各个一位数组中的内容。我们仍需要改变:我们可以按照下面程序中的方法去进行;

#include "stdio.h"

void main()

{

    int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};

    int (*p)[3] = a;

    printf("p[0]:%d/n",*p[0]);

    printf("p[1]:%d/n",*p[1]);

    printf("p[2]:%d/n",*p[2]);

    printf("*p+1:%d/n",*(*p));

    printf("*p+1:%d/n",*(*p+1));

    printf("*p+1:%d/n",*(*p+2));

    printf("*p+1:%d/n",*(*(p+1)));

    printf("*p+1:%d/n",*(*(p+1)+1));

    printf("*p+1:%d/n",*(*(p+1)+2));

    printf("*p+1:%d/n",*(*(p+2)));

    printf("*p+1:%d/n",*(*(p+2)+1));

    printf("*p+1:%d/n",*(*(p+2)+2));

}

输出结果:

 

这里我们须明白的是:

p指向的二维数组的首地址,目前它代表二维数组的首地址。

*p 代表第一个一维数组的地址。

*p+1代表第二个一维数组的地址。

……

**p则是代表第一个元素了。

*(*p+1)代表第二个元素.

剩下的也就顺理成章了….

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值