使用可变长度数组是否有一些开销?数组的大小可以在运行时通过命令行参数传递吗?与自动和动态分配数组相比,为什么要引入它?
4 回答
VLA 确实有一些开销(与“普通”命名的编译时大小的数组相比)。
首先,它具有运行时长度,但该语言为您提供了在运行时获取数组实际大小的方法(使用sizeof
)。这立即意味着必须将数组的实际大小存储在某个地方。这会导致一些微不足道的每阵列内存开销。然而,由于 VLA 只能被声明为自动对象,因此这种内存开销是任何人都不会注意到的。这就像声明一个额外的整数类型的局部变量。
其次,VLA 通常分配在堆栈上,但由于它的可变大小,通常情况下它在内存中的确切位置在编译时是未知的。出于这个原因,底层实现通常必须将其实现为指向内存块的指针。这引入了一些额外的内存开销(对于指针),由于上述原因,这又是完全无关紧要的。这也带来了轻微的性能开销,因为我们必须读取指针值才能找到实际的数组。这与访问 -ed 数组时获得的开销相同malloc
(而不是使用命名的编译时大小的数组)。
由于 VLA 的大小是运行时整数值,因此它当然可以作为命令行参数传递。VLA 不在乎它的大小来自哪里。
VLA 是作为运行时大小的数组引入的,具有低分配/解除分配成本。它们适合“普通”命名的编译时大小的数组(其分配释放成本几乎为零,但大小固定)和malloc
-ed 数组(具有运行时大小,但分配释放成本相对较高)。
VLA [几乎] 遵守与自动(即本地)对象相同的依赖于范围的生命周期规则,这意味着在一般情况下它们不能替换malloc
-ed 数组。它们的适用性仅限于需要具有典型自动生命周期的快速运行时大小的数组的情况。
可变长度数组存在一些运行时开销,但您必须相当努力地测量它。请注意,如果是可变长度数组,sizeof(vla)
则它不是编译时常量。vla
数组的大小可以在运行时传递给函数。如果您选择从命令行参数中获取大小并将其转换为整数并在运行时将其传递给函数,那就这样吧 - 它会起作用。
使用可变长度数组是因为变量会自动分配到正确的大小,并在退出函数时自动释放。这避免了过度分配空间(当您主要使用最小尺寸时,为最大可能尺寸分配足够的空间),并避免内存清理问题。
此外,对于多维数组,AFAIK的行为更像 Fortran - 您可以动态配置所有维度,而不是被固定在数组的前导维度之外的所有维度上。
VLA 的一些运行时开销的具体证据 - 至少在 SPARC (Solaris 10) 上使用 GCC 4.4.2。
考虑以下两个文件:
vla.c - 使用可变长度数组
#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);
size_t identity_matrix(int n, int m)
{
int vla[n][m];
int i, j;
assert(n > 0 && n <= 32);
assert(m > 0 && m <= 32);
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
vla[i][j] = 0;
}
vla[i][i] = 1;
}
return(sizeof(vla));
}
fla.c - 使用固定长度数组
#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);
size_t identity_matrix(int n, int m)
{
int fla[32][32];
int i, j;
assert(n > 0 && n <= 32);
assert(m > 0 && m <= 32);
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
fla[i][j] = 0;
}
fla[i][i] = 1;
}
return(sizeof(fla));
}
编译和目标文件大小
出于比较目的,本地数组的名称不同(vla
vs fla
),并且在声明数组时数组的维度也不同 - 否则,文件是相同的。
我编译使用:
$ gcc -O2 -c -std=c99 fla.c vla.c
目标文件的大小有些不同——用“ls”和“size”测量:
$ ls -l fla.o vla.o
-rw-r--r-- 1 jleffler rd 1036 Jan 9 12:13 fla.o
-rw-r--r-- 1 jleffler rd 1176 Jan 9 12:13 vla.o
$ size fla.o vla.o
fla.o: 530 + 0 + 0 = 530
vla.o: 670 + 0 + 0 = 670
我没有进行广泛的测试来查看有多少开销是固定的,有多少是可变的,但是使用 VLA 会有开销。
我只是想知道使用可变长度数组是否有一些开销?
没有
数组的大小可以在运行时通过命令行参数传递吗?
是的。
与自动和动态分配数组相比,为什么要引入它?
自动分配仅允许在编译时已知的固定大小。
动态分配 ( malloc
) 会将数组存储在堆上,堆内存空间大,但访问速度较慢。
VLA 通过将数组放入堆栈来工作。这使得分配和访问非常快,但堆栈通常很小(几 KB),并且当 VLA 溢出堆栈时,它与无限递归无法区分。
VLA 的开销应该很小(最多应该导致堆栈指针的添加)。动态分配需要手动内存管理并且比基于堆栈的 VLA 分配要慢,并且数组的“自动”声明需要数组大小的编译时表达式。但是,请记住,如果发生堆栈溢出,将导致未定义的行为,因此请保持 VLA 相对较小。
您可以通过命令行参数传递数组的大小,但您必须自己编写代码来处理它。