数组的深刻理解

文章详细阐述了数组的内存布局,指出它是线性连续且自下而上递增的。数组名作为右值代表数组首元素的地址,不能做左值。数组在函数传参时会发生降维,变成指向元素的指针,避免大规模拷贝,提高效率。一维和二维数组的示例进一步解释了这一概念。
摘要由CSDN通过智能技术生成

       数组(具有相同数据的集合,这里要明确数组内部可以放置任意类型的内容(包括数组))这样的观点将有利于我们去认知二维及更高维的数组,因为在内部空间中,其真正的内存布局并不是我们理想中的行列式结构,相反它像一条线,通过层层的隶属关系从而完成多维数组的内存布局。总而言之,即一句"数组的内存布局线性连续且递增"走天下(当有人问到为什么数组不能自减运算时,其答案不言自明。)

1.数组的内存布局

       如果定义连续的整形变量,会发现其在栈空间进行开辟空间,并且其首先定义的变量的地址是最大的,其他定义变量的地址随定义顺序而依此减少。但是这个规律对于数组并不适应,数组在栈空间是先开辟一块空间,然后自下而上的将每个数组元素的地址依次存放,这样会导致最先的数组元素的地址反而是最小的。综上可以一句话概括数组的空间布局:线性连续且自下而上的递增,且以一个整体去开辟空间和整体释放。

结论:数组的空间布局是线性连续且自下而上的递增,且以一个整体去开辟空间和整体释放。

2.&a[0]和&a的区别(这里假设a是数组)

       前者表示取数组首元素的地址,后者则是取数组的地址。这样从字面上理解很难,因为首先在地址大小上,两者是相同的。但是从操作过程来看,前者在进行加减操作时其地址值改变的是起始单位是数组大小整数倍,后者在进行加减操作时其地址值改变的是其指向该数组元素指针类型大小整数倍。在实际运用中,往往前者运用的更多,后者运用的情况有两种:一种是直接取地址:&a;第二种是计算sizeof(a).

总结:对指针加减,(在不发生类型转换情况下)其实是加减其指向类型的大小;对于二级指针及往上指针加减都是加减当前平台指针类型大小。

3.数组名a能做左值和右值?

       由int a[10]={0},int*p=&a可知,数组名可以做右值,代表的是数组首元素的地址,可以将其等价于&a[0]。但是由于不能对数组整体赋值(因为数组名是数组首元素的地址,其数据类型为指针,对于int a[10]=10这样的代码,左边是指针,右边却是整型,两边类型不同,又无法强制类型转化,因此会报错。),与左值可以作为空间存储的概念相悖。综上,数组名能做右值,不能做左值。

结论:数组名能做右值,不能做左值。

4.数组传参

       数组传参会发生降维,即降维成指针,即指向数组内部元素的指针。如果不发生降维,那么发生拷贝数组现象(几乎每个数组的内容是很大的),导致函数调用效率降低。在C语言中发生函数的调用,只要由形参实例化,就必定会形成临时拷贝。先看一段代码:void(int arr[ ],int num),请问这里arr[ ]中内容是否可以省略?答案是可以,但必须为非负数。因为数组传参会发生降维,那么必须确保降维后的指针类型明确,像这里一维数组传参,降维成int *p,如果是二维数组,例如arr[ ][ ]前面的【】和后面的【】是否可省略也是上面的逻辑,省略前面【】,其int *a[ ][6]等价于int(*a)[6],其降维后的指针明确;但当省略了后面的【】,其降维结果为int*p[ ],很明显无法判断其类型。像三维及以上的数组,皆是这个道理。

结论:数组传参会发生降维现象,其结果会降维成指向数组内部元素的指针,且必定会发生临时拷贝。

举例一:一维数组以及一维数组传参

        接下来我们利用上面所讲的理论去分析一些数组问题。首先看一段代码用以证明一维数组传参发生了降维现象:  

#include<stdio.h>
#include<windows.h>
void show(int a[10])
{
	printf("show:%d\n", sizeof(a));
}

int main()
{
	int a[10];
	printf("main:%d\n", sizeof(a));
	show(a);
	system("pause");
	return 0;
}

其输出结果为main为40,show为4。可以清晰地发现在一维数组传参后其a由原来的数组地址变成了指向数组首元素地址的指针(指针在32位平台大小为4个字节,在64位平台大小为8个字节大小,我是在32位下做的测试)

举例二:二维数组以及二维数组传参

<1>二维数组

数组可以包含数组是二维数组及高维数组产生的原因。所以可以理解二维数组可以被看成一维数组,只不过内部元素也是一维数组罢了。又数组的空间布局为线性连续且递增,所以对于二维数组整体也是线性连续且递增。下面是用图表示char[3][4]:

接下来我们来理解一些由此衍生的代码问题(这里举int a[3][4]):

 //计算二维数组的整个内存大小  4*3*4

//表示第一个数组元素中的数组首元素 为一个整型大小 4

//表示第一个数组元素 4*4

//表示第一个元素加当前元素大小后,跳到第二个元素中的首元素,第二个int  4

//表示解引用指向其第二个元素中的首元素 4

//表示 这里a是数组首元素的含义,及与第四个等价 4

//表示第二个元素的大小  4*4

//表示解引用指向其第二个元素中的首元素  4

//表示第一个数组元素 4*4

//表示第四个数组元素, 4*4(虽然没定义但不影响)

 自行对号入座即可

<2>二维数组传参

       根据前面所讲的数组传参,对于二维数组,内部元素是一维数组,那么降维成指向一维数组的指针(即我们"深恶痛绝"的数组指针),这里就不多详解了,再看一段代码用以证明以上观点:

#include<stdio.h>
#include<windows.h>
void show(char a[][4])
{
	printf("show:%d\n", sizeof(a));
}

int main()
{
	char a[3][4] = {0};
	printf("main:%d\n", sizeof(a));
	show(a);
	system("pause");
	return 0;
}

其结果main为12,show为4。具体情况和前面类似,这里就不多展开了。对于三维及更高维数组,建议画图去表示,这里本人功力尚浅,浅显地认为其高维数组的表示图类似于正立的松树状结构。

       以上就是我对数组的深刻理解,若有不足之处,希望各位看官不惜赐教。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只爱喝coke的小鳄鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值