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

标签 c

int a[n];

对比

int * a;
a = malloc(n * sizeof(int));

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

最佳答案

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

代码:

int a[n];

被编译器视为:

int T_elems = n;
typedef int T[T_elems];
T a;

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

int *b;
{
   int a[n];
   // `a` is valid
   b = a;
}
// resources pointed by `b` are released

另一方面,第二个片段:

int * a = malloc(n * sizeof(int));

创建一个指向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

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

int (*a)[n] = malloc(sizeof(int[n]));

总结:

  1. VLA(和 VM 类型)

优点:

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

缺点:

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

优点:

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

缺点:

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

组织成表格的相同数据:

<表类="s-表"> <头> <日> 自动 VLA malloc <正文> 语法 简单 更详细 错误行为 堆栈溢出 错误处理 不可能:失败时的行为未定义 必须检查malloc的返回值 最大尺寸 筹码量 堆大小(大于堆栈) 速度 非常快(比固定数组稍慢) 比任何基于堆栈的分配都慢 解除分配 自动;无泄漏风险 必须使用 free() 释放内存以避免泄漏 数组大小支持 是的,通过 sizeof 不行,程序员必须显式存储 可以返回吗? 没有 是 可以放入struct吗? 没有 是 便携性 C99,自C11起成为可选特性 所有C标准都支持 支持缩放吗? 没有 是的,通过realloc 多维度 语法简单 可能会混淆语法

关于c - 可变长度数组和动态内存分配有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67356818/

相关文章:

c - 使用C程序从外部打开文件

c - 在 c 中重新分配时出错(异常 trown (ucrtbased.dll))

c++ - 跟踪 C++ 中不可重现的错误

c - Asterisk :对 `SQLAllocHandle' 的 undefined reference

ios - HTML5 视频播放时 CALayerInvalidGeometry 异常

c - C11中const限定符的性能优势

c - 初始化局部变量,什么时候发生?

c - 程序通过将每个退格替换为\b来将其输入复制到输出

c - C中的递归仅在打印不相关的东西时有效

c - 我无法在 else 语句中使用 scanf()