数组在内存中如何分配:区分栈堆与静态区
数组在内存中如何分配,主要由数组的声明位置、存储类型、是否动态开辟决定,局部非静态数组默认分配在栈内存,全局数组、static修饰的静态数组分配在静态存储区,new、malloc创建的数组分配在堆内存,栈数组长度必须是编译期常量,栈空间容量通常较小,Windows环境下栈大小一般为1MB左右,超出会触发栈溢出,堆内存容量更大,由程序员手动管理释放,静态区数组在程序启动时分配、程序结束才释放,该分配规则适用于C、C++语言,Java、Python等托管语言数组底层机制不一样,不能直接套用这套判断方式。
数组在内存中如何分配:栈内存数组
栈内存上的数组随函数调用创建,函数执行结束自动回收内存,不需要手动释放。数组名代表数组首元素的地址,数组内部元素是连续紧凑排布,地址由低到高依次存放每个成员。你定义数组时方括号内的长度必须是编译阶段就能确定的常量,C99变长数组是特例,它语法允许变量指定长度,但依然存放在栈上,变长数组的兼容性在部分编译器上支持有限。
栈内存分配速度快。
数组在内存中如何分配:堆内存数组
堆内存数组使用malloc或者new关键字申请,申请成功后返回首地址,需要用指针接收,数组元素同样连续存放,但内存生命周期不受函数作用域约束。你用完之后必须调用free或者delete释放对应的内存,不及时释放会形成内存泄漏,程序长时间运行后可用堆空间持续变少。堆分配的开销高于栈,系统需要查找空闲内存块,分配耗时会明显更长。
数组在内存中如何分配:静态存储区数组
写在函数外部的全局数组、函数内部加static修饰的数组,都会放在静态存储区。这部分内存和栈、堆相互独立,初始化只执行一次,就算函数多次调用,数组只分配一次内存,里面的值会保留上一次修改后的结果。静态存储区分成初始化静态区和未初始化静态区,未显式初始化的数组元素会默认填充0。
不同内存区域数组对比
| 内存区域 | 分配方式 | 释放时机 | 长度限制 |
|---|---|---|---|
| 栈 | 自动分配 | 函数退出自动释放 | 偏小,常量长度为主 |
| 堆 | 手动申请 | 代码手动释放 | 容量大,支持变量长度 |
| 静态区 | 程序加载分配 | 程序退出释放 | 受程序全局内存限制 |
数组在内存里的排布规则统一,无论分配在哪一块区域,元素地址连续,数组名指向首元素地址,多维数组在内存中也会展开成一维连续序列,C、C++采用行优先展开,先存完一行全部元素再继续下一行。多维数组只是语法层面的包装,底层不存在多行分段存储。
数组名本身不是指针,在多数表达式场景下会隐式转为首元素指针,存在两个例外:sizeof(数组名)得到整个数组占用字节数,&数组名取到整个数组的地址,地址数值和首元素地址一致,但类型不相同,做加减运算时步长有区别。
变长数组不能作为全局变量,C标准不允许全局变长数组。
托管语言的数组由虚拟机自动托管内存,Java数组全部在堆上分配,基础类型数组存放数值,引用类型数组存放对象引用,Python列表不是原生数组,底层是动态扩容结构,元素地址并不连续,不能直接沿用C语言数组内存分配逻辑判断。
