深入理解C语言数组与内存分配
C语言在定义数组时是否允许使用变量指定数组长度,如果您的答案否,那我建议您仔细阅读以下这篇文章:)
概述
很多C语言教材都提到数组长度的定义必须是常量,为什么C语言数组会有这种限制呢?这就要从程序变量的内存分配开始说起了。
我么知道程序在运行时候数据、变量可能会存放的段有以下几个:
堆区:malloc分配的内存就在这个区中
栈区:程序调用时函数内部的局部变量在这个区中
.data区:这个区中的数据是程序开始运行前由操作系统的加载器将可执行文件加载进入内存时创建的,用于存放程序中定义的已初始化全局变量或者静态变量。
回到刚刚的问题,数组会存放到哪几个段中呢?
这个可能要和数组的位置有关系了,我们分成几种情况讨论。
定义为函数内局部变量
如果数组定义成函数中的局部变量,比如下面的代码:
void f1(int n)
{
int array[n];
int i;
for(i = 0; i < n; i++)
{
array[i] = i;
}
}
int main()
{
f1(1024);
}
代码中的数组,属于函数f1内的局部变量,根据概述中的描述应该内存应该开辟在栈中。
栈中的变量有一个很重要的特性就是当函数返回的后,变量占用的内存会被释放。结合栈的FILO的特性,对于一个函数如果需要申请临时变量仅仅需要将栈指针移动就很方便的为变量申请了空间。
回到这个问题如果在函数内部定义一个数组,而数组的长度是不固定的,长度是由参数n决定的是否可行呢?
答案是可行的,仅仅需要程序在运行时将程序传入的变量n*数组每个元素的长度就可以得出数组的长度。然后移动堆栈指针SP 数组长度就为数组分配出了内存。
事实上c89之后的编译器也的确允许这样定义。
这个特性相对于使用malloc函数还是有好处的,
- 不需要程序员考虑内存管理,不会造成内存泄漏。
- 效率相对于malloc的伙伴算法会快
- 不会造成内存碎片
定义为函数内静态变量
如果数组定义成函数内的静态变量,比如下面的代码:
void f1(int n)
{
static int array[n];
int i;
for(i = 0; i < n; i++)
{
array[i] = i;
}
}
int main()
{
f1(1024);
}
静态变量的内存时在程序加载时分配在.data段的,由于此时程序尚未运行,不能确定函数参数n的值,无法确定申请内存的大小。因此这种情况编译器不能通过。我们尝试拿该代码编译会报错
test.c:8:13: error: storage size of ‘array’ isn’t constant
static int array[n];
定义为全局变量
如果数组定义成全局变量呢,比如下面的代码:
int size = 10;
int array[size];
void f1(int n)
{
int i;
for(i = 0; i < n; i++)
{
array[i] = i;
}
}
int main()
{
f1(1024);
}
全局变量内存空间的申请也是需要在程序加载初期由加载器初始化,这种情况下的数组内存分配到.data段中,基于上段相似的理由,编译器无法支持,会报如下错误:
test.c:5:5: error: variably modified ‘array’ at file scope
int array[size];
^
结论
C语言在定义数组时是否允许使用变量指定数组长度呢?
答案是分情况,
如果数组定义在函数内部时,编译器会力所能及的帮我们实现变量长度定义数组。
其他的情况由于内存空间在程序加载时就需要知道数组占用的空间大小,所以编译器无计可施只能报错。