【问题标题】:Variable-length arrays are created on heap, but we cannot free them?变长数组是在堆上创建的,但我们不能释放它们?
【发布时间】:2015-11-04 18:18:27
【问题描述】:

我已经验证了可变长度数组是在堆上创建的(参见下面的代码),但我们不能使用空闲操作来释放它们(导致故障陷阱 6)。

我被告知堆是由用户管理的,因此如果我们不需要它们,我们必须显式地释放堆上的任何东西。那么谁来负责释放这些记忆呢?这是C语言的缺陷吗?


显示可变长度数组的代码是在堆上创建的。 (平台:Mac OS X,gcc,64位)

#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv)
{
    int n = atoi(argv[1]);
    int a1[10];
    int a2[n];
    int a3[10];

    printf("address for a1:%p,address for a2:%p, address for a3:%p\n",a1,a2,a3);
    printf("a1-a2: %lx, a1-a3: %lx\n",(a1-a2),(a1-a3));

    //free(t); // will cause fault trap 6

    return 0;
}

结果是:

$ ./run 10
address for a1:0x7fff5d095aa0,address for a2:0x7fff5d0959e0, address for a3:0x7fff5d095a70
a1-a2: 30, a1-a3: c

很明显 a1 和 a3 是连续的,因此在堆栈上,但 a2 的地址较低,因此在堆上。

【问题讨论】:

  • 否 - 在这种特殊情况下,VLA 位于堆栈中,但它位于其他局部变量之后(出于显而易见的原因)。但是,无论它位于何处,尝试显式释放它都是错误的。
  • @PaulR Quick,蝙蝠侠,回答问题!
  • “可变长度数组是在堆上创建的”——不。如果你printf("%p", malloc(1)); 它可能会从你的静态大小的局部变量和你的 VLA 中产生一个 far away 的地址。 VLA 之前/之后的额外空间可能只是对齐/填充。
  • 我投票结束这个问题,因为它是基于一个错误的前提。
  • @Quentin:呵呵——我不确定这个问题是否值得一个完整的答案,因为它基于一个错误的前提(如上所述!),因此可能应该被关闭。

标签: c arrays


【解决方案1】:

可变长度数组具有自动存储持续时间,并且具有块范围或函数原型范围。

所以这个数组

int a2[n];

具有自动存储持续时间和函数main的块范围。它不是在堆中创建的。当控件退出块作用域时,是编译器生成相应的代码来释放为数组分配的内存。

根据 C 标准(6.2.4 对象的存储时长)

7 对于这样一个具有可变长度数组类型的对象,它的 生命周期从对象的声明到执行 程序离开声明的范围。35) 如果范围是 以递归方式输入,每个对象都会创建一个新实例 时间。对象的初始值是不确定的。

您只能将函数 free 应用于使用 malloc、calloc 或 realloc 等内存分配函数之一分配的对象。

【讨论】:

    【解决方案2】:

    实现如何为 VLA 分配存储空间由实现定义。 VLA 具有自动存储期限,您不应该尝试free() 它。 出于所有实际目的,您应该像对待任何其他局部变量一样对待它。

    无论您使用 malloc() 系列函数分配的内存如何,您都只能 free()。

    并非所有实现都支持 VLA,它是一个有条件的功能。

    _ _STDC_NO_VLA_ _
    

    用于测试实现是否支持 VLA(如果为 1 则不支持 VLA)。

    在我看来,不应该使用 VLA 主要是因为:

    • 它们在 C11 中是可选的
    • 分配失败不可移植检测

    【讨论】:

      【解决方案3】:

      这是C语言的缺陷吗?

      绝对不是。你做了几个错误的假设,导致你得出错误的结论。

      首先,我们来搞清楚术语:“栈”被称为自动存储区; “堆”被称为动态存储区。 C 标准不对下面列出的任何内容做出任何声明:

      • 自动和动态区域中地址的相对顺序
      • 同一存储区域内项目地址的相对顺序
      • 同一区域内的分配之间是否存在差距

      这使得仅通过查看数字地址就无法确定变量是在自动区域还是在动态区域中,而无需进行猜测。特别是,对您来说“显而易见”的事情与实际发生的事情无关。

      那么谁来负责释放这些记忆呢?

      您有责任对您在动态存储区中分配的所有内容调用free。你没有在动态存储区分配你的变长数组*,因此你不负责在它上面调用free

      * 如果编译器实现要在动态存储区分配 VLA,编译器将负责在该指针上调用 free

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-16
        • 2020-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-17
        相关资源
        最近更新 更多