使用可变长度数组有什么开销吗?
有使用变长数组的开销吗? 数组的大小可以在运行时通过命令行parameter passing吗? 为什么它被引入,相比自动和dynamic分配一个数组?
VLA确实有一些开销(与“普通”命名的编译时数组相比)。
首先,它具有运行时间长度,但语言为您提供了在运行时获取数组实际大小的方法(使用sizeof
)。 这立即意味着数组的实际大小必须存储在某处。 这会导致一些微不足道的每arrays内存开销。 但是,由于VLA只能被声明为自动对象,所以这种内存开销并不是任何人都会注意到的。 这就像声明一个整型的额外局部variables一样。
其次,VLA通常在堆栈上分配,但是由于VLA的大小不同,一般情况下,它在存储器中的确切位置在编译时是不知道的。 基于这个原因,底层实现通常必须将其实现为一个指向内存块的指针。 这引入了一些额外的内存开销(对于指针),由于上述原因,这又是完全不重要的。 这也引入了轻微的性能开销,因为我们必须读取指针值才能find实际的数组。 这与访问malloc
-ed数组(而不使用指定的编译时数组)时得到的开销相同。
由于VLA的大小是一个运行时的整数值,所以它当然可以作为命令行parameter passing。 VLA不关心它的大小来自哪里。
VLA作为运行时大小的arrays引入,具有较低的分配/释放成本。 它们适合于“普通的”指定的编译时大小的数组(具有几乎为零的分配 – 解除分配成本,但固定大小)和malloc
-ed数组(具有运行时间大小,但分配 – 解除分配成本相对较高)。
VLA遵循与自动(即本地)对象几乎相同的依赖于范围的生存规则,这意味着在一般情况下,它们不能替代malloc
-ed数组。 它们的适用性仅限于需要具有典型自动生命周期的快速运行时大小的arrays的情况。
在可变长度数组中有一些运行时间的开销,但是你必须相当努力地测量它。 请注意,如果vla
是可变长度数组,则sizeof(vla)
不是编译时常量。
数组的大小可以在运行时传递给函数。 如果您select从命令行参数中获取大小,并将其转换为整数,并在运行时将其传递给函数,那就这样做 – 它将起作用。
因为variables被自动分配到正确的大小,并在退出函数时自动释放,所以使用变长数组。 这样可以避免过度分配空间(在大多数情况下使用最小的大小时,为可能的最大大小分配足够的空间),并避免内存清理问题。
此外,对于multidimensional array, AFAIK的行为更像Fortran – 您可以dynamicconfiguration所有维度,而不是使用固定大小卡住除数组的前导维以外的所有维。
VLA的一些运行开销的具体证据 – 至less在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
我没有做过广泛的testing,看看有多less开销是固定的,有多less是可变的,但是使用VLA会有开销。
我只是想知道是否有使用可变长度数组的开销?
不
数组的大小可以在运行时通过命令行parameter passing吗?
是。
为什么它被引入,相比自动和dynamic分配一个数组?
自动分配只允许在编译时已知的固定大小。
dynamic分配( malloc
)会将数组存储在堆上 ,这个堆的内存空间很大,但访问速度较慢。
VLA通过将数组放入堆栈工作 。 这使得分配和访问非常快, 但是堆栈通常很小(几KB),并且当VLA溢出堆栈时,它与无限recursion无法区分。
对于VLA应该有很less的开销(最多它应该导致堆栈指针的增加)。 dynamic分配需要手动内存pipe理,并且比基于堆栈的VLA分配要慢,数组的“自动”声明需要编译时间expression式来存储数组大小。 但是请记住,如果发生堆栈溢出,会导致未定义的行为,所以保持VLA相对较小。
你可以通过命令行parameter passing一个数组的大小,但是你必须编写代码来处理它自己。