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 在内的自动存储持续时间对象(通过大多数现代实现)分配在堆栈上。如果在栈上分配较大的对象会出现一些问题(三个最重要的):

  1. 没有分配控制,除非你的程序在溢出堆栈时失败
  2. 堆栈通常比堆小得多。所以数组的大小是有限的
  3. 数组的生命周期限于封闭块的生命周期。您不能 return 在函数 return.
  4. 上引用此数组

问这两个问题 “我的数组大小适合堆栈吗?”“我需要这个数组多长时间(程序或函数寿命时间)?".

静态分配的数组在函数的生命周期内存在。当 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。是的,有点自相矛盾。