C 中可变长度数组的动态内存分配
Dynamic memory allocation in C for variable length arrays
什么时候使用 calloc 实例化可变长度数组与 C 中的“普通”数组声明相比更好?
考虑 'array declaration' 方法:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int n = atoi(argv[1]);
int x[n];
for(int i = 1; i < n; i++){
x[i] = i;
printf("%i", x[i]);
printf("\n");
}
return 0;
}
对比calloc 方法:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int n = atoi(argv[1]);
int * x = (int*) calloc(n, sizeof(int));
for(int i = 1; i < n; i++){
x[i] = i;
printf("%i", x[i]);
printf("\n");
}
return 0;
}
你应该总是使用其中之一吗?一个比另一个快吗(例如堆栈的 bc 与堆分配)?一个比另一个风险大吗?
最大的区别在于,在第一个示例中,数组仅在包含函数的生命周期内存在,但在第二个示例中,它一直存在直到被释放。
在你的例子中只有 main 函数所以差异并不重要,但在实际应用中它很重要。
其次,第一个的大小是有限制的,因为它分配在堆栈上,这是一种有限的资源。 (尝试一下,使数组包含 100 万个元素)。第二种情况仅受堆大小的限制,通常要大得多
int x[n];
包括 VLA 在内的自动存储持续时间对象(通过大多数现代实现)分配在堆栈上。如果在栈上分配较大的对象会出现一些问题(三个最重要的):
- 没有分配控制,除非你的程序在溢出堆栈时失败
- 堆栈通常比堆小得多。所以数组的大小是有限的
- 数组的生命周期限于封闭块的生命周期。您不能 return 在函数 return.
上引用此数组
问这两个问题 “我的数组大小适合堆栈吗?” 和 “我需要这个数组多长时间(程序或函数寿命时间)?".
静态分配的数组在函数的生命周期内存在。当 return 时,函数堆栈(以及声明为该函数本地的任何变量,包括您的数组)被销毁,并且任何访问它们的尝试都会产生未定义的行为。
但是,如果您确实需要 return 指向数组的指针,以便您存储的值在调用函数中可用,那么您确实需要动态分配它,当您动态分配内存时使用 calloc 时,生命周期从内存分配开始延长,直到它被释放。
请记住,静态分配的内存比动态分配的内存更快、更高效,所以如果您真的不需要它,请不要花哨。
实现有条件地支持可变长度数组。
如果定义了宏名__STDC_NO_VLA__
则整型常量1,意在表示该实现不支持变长数组或可变修饰类型
另一个问题是堆栈内存通常比用于动态内存分配的内存少很多。
可变长度数组的大小可以自动改变,然后控件重新到达数组的声明。
可变长度数组具有自动存储持续时间和块作用域。所以它们在定义它们的块作用域中仍然存在。
动态分配的数组可以使用函数 realloc 调整大小,保留其元素的当前值,并且当没有足够的 space 用于动态分配的数组时,您可以在程序中明确控制情况。
一般来说,只有局部 typedef
of multi-dimensional 数组才能真正安全地使用可变长度数组。其他任何事情都是可疑的。类似于:
void *buf = calloc(a*b, sizeof(int));
typedef int Int2d[a][b];
Int2D *array2d = buf;
(*array2d)[y][x] = 42;
所以使用calloc
版本。只有当你有一个已知的小尺寸时,才直接使用 VLA。是的,有点自相矛盾。
什么时候使用 calloc 实例化可变长度数组与 C 中的“普通”数组声明相比更好?
考虑 'array declaration' 方法:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int n = atoi(argv[1]);
int x[n];
for(int i = 1; i < n; i++){
x[i] = i;
printf("%i", x[i]);
printf("\n");
}
return 0;
}
对比calloc 方法:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int n = atoi(argv[1]);
int * x = (int*) calloc(n, sizeof(int));
for(int i = 1; i < n; i++){
x[i] = i;
printf("%i", x[i]);
printf("\n");
}
return 0;
}
你应该总是使用其中之一吗?一个比另一个快吗(例如堆栈的 bc 与堆分配)?一个比另一个风险大吗?
最大的区别在于,在第一个示例中,数组仅在包含函数的生命周期内存在,但在第二个示例中,它一直存在直到被释放。
在你的例子中只有 main 函数所以差异并不重要,但在实际应用中它很重要。
其次,第一个的大小是有限制的,因为它分配在堆栈上,这是一种有限的资源。 (尝试一下,使数组包含 100 万个元素)。第二种情况仅受堆大小的限制,通常要大得多
int x[n];
包括 VLA 在内的自动存储持续时间对象(通过大多数现代实现)分配在堆栈上。如果在栈上分配较大的对象会出现一些问题(三个最重要的):
- 没有分配控制,除非你的程序在溢出堆栈时失败
- 堆栈通常比堆小得多。所以数组的大小是有限的
- 数组的生命周期限于封闭块的生命周期。您不能 return 在函数 return. 上引用此数组
问这两个问题 “我的数组大小适合堆栈吗?” 和 “我需要这个数组多长时间(程序或函数寿命时间)?".
静态分配的数组在函数的生命周期内存在。当 return 时,函数堆栈(以及声明为该函数本地的任何变量,包括您的数组)被销毁,并且任何访问它们的尝试都会产生未定义的行为。
但是,如果您确实需要 return 指向数组的指针,以便您存储的值在调用函数中可用,那么您确实需要动态分配它,当您动态分配内存时使用 calloc 时,生命周期从内存分配开始延长,直到它被释放。
请记住,静态分配的内存比动态分配的内存更快、更高效,所以如果您真的不需要它,请不要花哨。
实现有条件地支持可变长度数组。
如果定义了宏名__STDC_NO_VLA__
则整型常量1,意在表示该实现不支持变长数组或可变修饰类型
另一个问题是堆栈内存通常比用于动态内存分配的内存少很多。
可变长度数组的大小可以自动改变,然后控件重新到达数组的声明。
可变长度数组具有自动存储持续时间和块作用域。所以它们在定义它们的块作用域中仍然存在。
动态分配的数组可以使用函数 realloc 调整大小,保留其元素的当前值,并且当没有足够的 space 用于动态分配的数组时,您可以在程序中明确控制情况。
一般来说,只有局部 typedef
of multi-dimensional 数组才能真正安全地使用可变长度数组。其他任何事情都是可疑的。类似于:
void *buf = calloc(a*b, sizeof(int));
typedef int Int2d[a][b];
Int2D *array2d = buf;
(*array2d)[y][x] = 42;
所以使用calloc
版本。只有当你有一个已知的小尺寸时,才直接使用 VLA。是的,有点自相矛盾。