变长数组和动态内存分配有什么区别?

rea*_*hao 3 c

int a[n];
Run Code Online (Sandbox Code Playgroud)

int * a;
a = malloc(n * sizeof(int));
Run Code Online (Sandbox Code Playgroud)

谁能分别解释一下这两种方法的优缺点?(效率、安全性等)

tst*_*isl 5

主要区别是 VLA 声明引入了新的可变修改(VM)类型,并且对象具有自动存储,变体malloc()具有动态存储。

代码:

int a[n];
Run Code Online (Sandbox Code Playgroud)

编译器将其视为:

int T_elems = n;
typedef int T[T_elems];
T a;
Run Code Online (Sandbox Code Playgroud)

由于结果数组a具有自动存储,并且在现代系统上分配在堆栈上。自动意味着当离开对象被引入的范围时,对象的内存被释放。

int *b;
{
   int a[n];
   // `a` is valid
   b = a;
}
// resources pointed by `b` are released
Run Code Online (Sandbox Code Playgroud)

另一方面,第二个片段:

int * a = malloc(n * sizeof(int));
Run Code Online (Sandbox Code Playgroud)

创建一个指向它的指针int并为其分配一个动态分配的对象。即使范围a已经结束,对象资源仍然有效。内存在用 释放之前一直有效free()

int *b;
{
   int * a = malloc(n * sizeof(int));
   b = a;
}
// resources pointed by `b` are valid!
free(b);
// memory is no longer valid
Run Code Online (Sandbox Code Playgroud)

有一个常见的误解,认为 VLA 总是分配在堆栈上。VLA 可以通过指向数组的指针在堆上分配:

int (*a)[n] = malloc(sizeof(int[n]));
Run Code Online (Sandbox Code Playgroud)

概括:

  1. VLA(和 VM 类型)

优点:

  • 自动VLA的简单分配
  • “always”成功(C 标准未定义的失败行为)
  • 分配非常快(比固定数组稍慢)
  • 自动 VLA 无泄漏风险
  • 可以有动态存储(通过指向VLA的指针)
  • 对于多维数组非常方便
  • 携带其大小从而sizeof a返回n * sizeof int

缺点:

  • 自动 VLA 很容易溢出堆栈
  • 无法在文件范围定义 VM 类型(无法返回)
  • VM 类型不能用作结构成员
  • 自C11起成为可选功能
  • sizeof评估 VLA 类型的操作数
  • 自动 VLA 无法调整大小
  1. 动态一维数组。

优点:

  • 受所有 C 标准支持
  • 可以是任意大小,没有堆栈限制
  • 指示分配是否成功,但是在现代操作系统上malloc()保留了地址空间。操作系统几乎不能保证内存保留
  • 可以调整大小realloc()

缺点:

  • 更详细的语法
  • 必须检查是否成功
  • 分配比任何基于堆栈的分配都慢
  • 内存必须释放以free()避免泄漏
  • 不携带大小,必须保留数组的大小

相同的数据组织为表格:

自动VLA malloc
句法 简单的 更详细
错误时的行为 堆栈溢出 NULL
错误处理 不可能:失败时的行为未定义 必须检查 的malloc返回值
最大尺寸 堆栈大小 堆大小(大于堆栈)
速度 非常快(比固定数组稍慢) 比任何基于堆栈的分配都慢
解除分配 自动的; 无泄漏风险 内存必须用 free() 释放以避免泄漏
数组大小支持 是的,由sizeof 不,程序员必须显式存储
可以退货吗? 是的
可以放进去struct吗? 是的
可移植性 C99,自C11起成为可选功能 受所有 C 标准支持
支持调整大小吗? 是的,由realloc
多维 语法简单 可能令人困惑的语法