【问题标题】:Understanding stack-allocated objects deallocation了解堆栈分配的对象释放
【发布时间】:2018-11-19 07:40:49
【问题描述】:

我试图了解释放堆栈分配对象的行为方式。准确地说,我试图在标准(N1570)中找到解释。考虑以下简单函数:

void foo(){
    char test[4096];
    test[10] = 0;
}

这里test 数组将在foo 退出时被释放。在objdump 中很容易看到test 分配在堆栈上。标准(强调我的)规定:

一个对象,其标识符被声明为没有链接,也没有 存储类说明符 static 具有自动存储持续时间,同样如此 一些复合文字。

所以test 具有自动存储期限。我们可以很容易地改写函数如下:

void test(){
    char *test= malloc(4096 * sizeof(char));
    test[10] = 0;
    free(test);
}

但我们必须自己解除分配,而test 仍然有自动存储时长

问题:标准如何规定char test[4096] 将在函数退出时被释放?标准没有声明test 是在堆栈上分配的,它是由实现定义的。

【问题讨论】:

  • 标准没有指定它被释放,只是内存变得无效。
  • 在第二种情况下,您分配的是 指向 char 的指针,而不是数组的内存。数组和指针不是一回事。因此,在您的情况下,唯一具有自动存储功能的变量是指针test,它将在函数退出时被释放。 malloc 分配的内存是从堆中获取的,是永久内存(直到没有被显式释放)。

标签: c arrays memory-management


【解决方案1】:

该标准在§6.2.4描述了各种存储持续时间

1 对象具有决定其生命周期的存储持续时间。那里 有四个存储持续时间:静态、线程、自动和分配。 7.22.3 中描述了分配的存储空间。

2 对象的生命周期是程序执行期间的一部分 保证为它保留哪些存储空间。对象存在, 有一个常量地址,并保留其最后存储的值 在其整个生命周期。如果一个对象在其外部被引用 生命周期,行为未定义。指针的值变为 不确定它指向(或刚刚过去)的对象何时到达 生命周期结束。

6 对于这样一个没有变长数组类型的对象, 它的生命周期从进入它所在的块开始 关联,直到该块的执行以任何方式结束。 (输入一个 封闭的块或调用函数挂起,但没有结束, 执行当前块。)如果块是递归进入的, 每次都会创建一个新的对象实例。初始值 对象的不确定性。如果指定了初始化 对象,每次声明或复合时都会执行 在块的执行中达到文字;否则,值 每次达到声明时都变得不确定。

所以你说得很对。它根本没有描述何时以及如何释放存储本身。它仅指定何时可以使用明确定义的语义访问该存储。实现不需要立即释放具有自动存储持续时间的变量的存储空间,如果您希望您的程序符合标准,则无法触及它。

对于分配的存储也是如此,另外需要注意的是,您必须明确告诉实现您已完成存储。但即使你“释放”它,实现也可能会保留它一段时间。

在纸面上可能存在一个非常糟糕的实现,它永远不会释放内存。但在实践中,这些自然而然地被淘汰了,因为 C 的糟糕实现只是被大众弃用并被抛弃。

【讨论】:

  • 所以这意味着我的第二个例子错了。 test 在那里分配了存储持续时间,对吧?
  • @St.Antario - 不是test,而是*test。指针具有自动存储持续时间。 pointee 已分配持续时间。这可能看起来像分裂头发,但却是一个重要的区别。
【解决方案2】:

这是关于从分配器函数返回的已分配内存的生命周期。对于内存分配器函数,明确指出(引自C11,第 7.22.3.1 章,强调我的)

[...] 如果分配成功,则返回的指针经过适当对齐,以便可以分配给 指向具有基本对齐要求的任何类型对象的指针,然后使用 在分配的空间中访问这样的对象或这样的对象数组(直到空间 被显式释放)。已分配对象的生命周期从分配开始 直到解除分配。 [...]

因此,您需要显式释放分配的内存。除非您明确取消分配,否则分配的内存仍然有效以供使用(如果处理不当,随后会导致memory leak)。

OTOH,在第二个 sn-p 中,变量 test 仍然超出函数出口的范围,因为它具有自动存储功能。 变量test(注意:不是test 指向的内存)分配的内存不再有效,并且尝试访问这将是未定义的行为。请记住,变量test 也是如此,即在函数返回后,&test 变得无效,但是,因为test 指向的内存是通过分配器函数分配的,返回该指针并从函数调用仍然是有效的访问。

再次引用规范,

[...] 如果一个对象在其外部被引用 生命周期,行为未定义。 [...]

【讨论】:

    【解决方案3】:

    第一个示例中的 test 变量是一个 4096 字节的数组。

    第二个示例中的test只是一个指针变量(4/8 字节)您使用从堆空间中从malloc(3) 获得的指针值进行初始化。

    test 变量(分别为 4096 和 4/8 字节)确实会在您的程序离开例程时自动释放,但您在第二个示例中额外分配了 4096 字节,来自堆,C 语言不知道(它们被分配在一个没有什么特别之处的库例程中,因此如果您不明确地这样做,它们不会自动返回到堆中。---堆是malloc() 已知的特殊内存位置,它允许您以任何顺序获得额外的内存,并且也可以以任何顺序返回---)

    【讨论】:

      猜你喜欢
      • 2011-06-16
      • 2020-10-03
      • 2013-01-25
      • 1970-01-01
      • 2011-05-23
      • 1970-01-01
      • 2021-03-20
      • 2018-06-01
      • 2018-02-23
      相关资源
      最近更新 更多