C 理解堆栈分配对象的释放

C 理解堆栈分配对象的释放,c,arrays,memory-management,C,Arrays,Memory Management,我试图了解堆栈分配对象的释放行为。准确地说,我试图在标准()中找到一个解释。考虑下面的简单函数: void foo(){ char test[4096]; test[10] = 0; } 这里,test数组将在退出foo时解除分配。在objdump中很容易看到test是在堆栈上分配的。标准(重点矿山)规定: 一种对象,其标识符声明时没有链接,也没有 存储类说明符static具有自动存储持续时间 一些复合文字 因此,test具有自动存储持续时间。我们可以很容易地重写函数,如下所示

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

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
仍然具有自动存储持续时间


问题:标准如何规定
字符测试[4096]
将在函数退出时解除分配?标准没有说明
测试
是在堆栈上分配的,它是实现定义的。

这是关于从分配器函数返回的已分配内存的生存期。对于内存分配器功能,明确规定(引用
C11
,第§7.22.3.1章,重点)

[…] 如果分配成功,则返回的指针已适当对齐,以便可以将其分配给 指向具有基本对齐要求的任何类型对象的指针,然后使用 在分配的空间中访问此类对象或此类对象的数组(直到 显式解除分配)。分配对象的生存期从分配开始延长 直到解除分配。[…]

因此,您需要显式地释放分配的内存。除非显式解除分配,否则分配的内存将保持有效以供使用(如果处理不正确,则会导致错误)

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

再次引用规范

[…]如果对象在其外部引用 在生命周期中,行为未定义。[……]


本标准描述了现场的各种存储持续时间

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

2.对象的生存期是程序执行过程中的一部分 保证为其保留的存储空间。存在一个对象, 具有恒定地址,并保留其上次存储的值 在它的一生中。如果在对象的外部引用对象 在生命周期中,行为未定义。指针的值变为 不确定其指向(或刚刚过去)的对象何时到达 生命的尽头

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

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

对于已分配的存储,情况也是如此,但增加了一条警告,即必须明确告知您已完成了对存储的实现。但是,即使您“释放”了它,一个实现也可能会保留它一段时间


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

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

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

test
变量(分别为4096和4/8字节)确实会在程序离开例程时自动释放,但是在第二个示例中,您从堆中额外分配了4096字节,C语言对此一无所知(它们是在没有特殊功能的库例程中分配的,因此,如果不显式地执行此操作,它们不会自动返回到堆。--堆是一个特殊的内存位置,malloc()知道,它允许您以任何顺序获取额外内存,也可以以任何顺序返回。-

标准没有规定