【问题标题】:How does free() affect the memory address on the heap?free() 如何影响堆上的内存地址?
【发布时间】:2019-03-27 19:39:39
【问题描述】:

这个赋值要求我们使用malloc()(命名为var1var2)分配两个int型变量,打印每个变量的地址(栈上指针的地址和堆上的地址),然后使用free() 释放var1,再次打印地址,然后在堆中为var1 分配另一个空间并第三次打印地址。我相信讲师正试图向我们展示var1 的堆地址应该改变,但它始终保持不变......除非我从代码中删除free(var1)。讲师做了一个类似的演示,但没有使用free() 来释放任何变量,所以我们从来没有看到它应该如何工作。

这是我的代码:

#include <stdio.h>
#include <stdlib.h>

void main()
{

int *var1 = (int*)malloc(sizeof(int)); 
*var1 = 1000;                   
int *var2 = (int*)malloc(sizeof(int)); 
*var2 = 2000;

printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2);

free(var1);

printf("AFTER DEALLOCATING var1 FROM THE HEAP\n");
printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2);

var1 = (int*) malloc(sizeof(int));
*var1 = 1500;

printf("NEW MEMORY ADDRESS ALLOCATED FOR var1\n");
printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2); 

}

此代码导致以下输出:

Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

AFTER DEALLOCATING var1 FROM THE HEAP
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

NEW MEMORY ADDRESS ALLOCATED FOR var1
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

如您所见,当我解除分配var1 时,堆地址没有改变,当我再次为var1 分配内存空间时,它也没有改变。但是,如果我只是从程序中删除free(var1) 行,它只是为var1 分配第二个内存空间并指向堆上的那个,它确实有不同的内存地址:

Addresses of var1 
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

AFTER DEALLOCATING var1 FROM THE HEAP
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

NEW MEMORY ADDRESS ALLOCATED FOR var1
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000420

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

(为了清楚起见,我所做的只是从前面的代码中删除了free(var1),因此“AFTER DEALLOCATING var1”部分现在显示的堆地址与前面的集合完全相同,但它确实改变了var1 在第三部分。)

谁能告诉我这里发生了什么?我能想到的唯一合乎逻辑的解释是,当我使用free() 释放var1 然后打印地址时,它只是打印它指向的最后一个地址,然后当我为var1 第二次,它只是用var1 的新值“回填”之前的地址。这有意义吗?我的代码中是否有错误,或者这正是 C 在为变量释放内存然后重新分配它时的行为方式?

【问题讨论】:

  • 1. free() 接受一个指针,您可以提供一个变量。但是,它不会更改该变量的内容。 (指针由指向free() 的值给出。) 2. 如果你free() N 字节和稍后(而是立即)malloc() N 字节再次,你为什么不应该得到相同的地址?听起来,堆管理做得很好——它重新使用了足够大小的可用空间。 ;-)
  • malloc 实现为相同大小的新对象重用空间是非常有意义的。
  • 你好。不是您要的,但您不再需要cast the return value of malloc。另外,我发现使用int *var = malloc(sizeof *var) 而不是int *var = malloc(sizeof(int)) 很有用。这样我就可以更改 var 的类型,而不会忘记在 malloc 调用中更改它(例如 float *var = malloc(sizeof(int)))。
  • 你基本上是对的。这里的教训是,即使在释放内存之后,您可能仍然有指向该内存的变量,尽管您不拥有它。这些“悬空指针”会导致很多痛苦,这就是为什么清除此类指针被认为是一种好的做法,例如free(p);p=NULL;

标签: c malloc heap-memory free memory-address


【解决方案1】:

当内存被释放然后重新分配时,malloc 可能返回相同的地址是完全正常的。它返回不同的地址也是正常的。

如果您更改 malloc 调用以请求与原始分配不同的大小,您可能会获得不同的地址,因为 malloc 准备的旧块可能不足以满足新请求。但它们可能就足够了,所以地址可能不会改变。

顺便说一句:

  • void main() 不正确。应该是int main(void)
  • C 标准不支持在释放地址指向的空间后打印地址。它“工作”并不少见,但它是不合适的。 C 2018 6.2.4 2 告诉我们“当指针指向(或刚刚过去)的对象达到其生命周期结束时,指针的值变得不确定。”当使用malloc 分配的对象被free 释放时,它的生命周期结束。

【讨论】:

  • 在地址指向的空间被释放后打印地址...你不能打印任何地址吗(只要你不访问内容,如果不是一个有效的地址)?
  • @Scheff:不。C 标准不要求指针是平面地址空间中的简单数字地址。一些 C 实现使用复杂的寻址方案(例如,段和偏移量,还有其他),这些可能需要指针的一部分是“活动的”,因为它引用了一些其他地址或寄存器或动态设置的结构向上。当空间被释放时,其他信息可能会被解构。然后,当printf 尝试为%p 格式化指针时,它使用该信息的尝试可能会失败。
  • 谢谢。我发现这是一个有趣的观点,值得在您的回答中提及。它有点超出“它只是一个可以打印的值”并且对于习惯于扁平地址而不会造成伤害的人来说很有趣。 (段/偏移地址让我想起了 8086 上的古老 DOS,但即使在那种情况下,我也不记得打印无效地址是多么危险。)
【解决方案2】:

我能想到的唯一合乎逻辑的解释是,当我使用 free() 释放 var1 然后打印地址时,它只是打印它指向的 LAST 地址

有点对。释放指针根本不会影响指针的内容。或者更准确地说,它的价值在被释放后是不确定的。事实上,根据标准,您甚至不能相信指针包含它在调用free 之前所做的地址。据我所知,在大多数情况下它会,但你不能相信它。

标准说:

C 2018 6.2.4 2:“当指针指向(或刚刚过去)的对象达到其生命周期结束时,指针的值变得不确定。”

一个常见的错误是使用测试if(ptr == NULL) 来检查您是否已正确释放指针。这行不通。

【讨论】:

  • C 标准不保证“释放指针根本不会影响指针的内容”。将指针传递给free 后,使用其值具有未指定的行为。
  • @EricPostpischil 但free(pointer) 如何更改pointer 的值?确定释放后,该值没有意义,但pointer 的位模式将保留。毕竟free 只是一个函数调用。但是,是的,我想不出一个用例可以将这个无意义的值用于任何有用的事情。
  • @EricPostpischil free 无法更改传递给它的指针的值。因此它将保持与以前相同的值,以前有效并且没有陷阱表示。当然不能再访问该地址的内容,但那是另一回事了。这似乎是该标准信息性附录 J 中的又一个错误。没有规范性文本说指针采用不确定的值或类似的值。
  • @Jabberwocky:free 无法更改代表pointer 的字节,但它可以更改用于组织内存和寻址的数据结构。请参阅我对我的回答的评论。
  • @EricPostpischil 啊,你是对的,我读错了。你应该在你的答案中加上引号。
【解决方案3】:

在第一个示例中,用于指向堆的指针的值在free() 之后在堆栈中保持不变。但您无法再访问该地址。

当他们再次分配变量时,您得到相同的地址并不奇怪,因为在该地址应该有一个大小合适的可用段。但是,您不能保证得到相同的地址——从可用的地址中最好的地址是由库代码处理的。

【讨论】:

    猜你喜欢
    • 2019-10-29
    • 2011-02-11
    • 1970-01-01
    • 2012-11-02
    • 1970-01-01
    • 1970-01-01
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多