【问题标题】:Freeing a pointer to a 'double' value释放指向“双”值的指针
【发布时间】:2014-06-22 03:20:52
【问题描述】:

我能找到的所有关于释放“双”指针的东西都是指释放指向指针的指针,这不是我要说的。我已经想出了如何做到这一点。

这可能根本不是什么大事,但我注意到当我 free() 一个 intcharfloat 指针时,该值被删除或替换为垃圾值,但是当我 free() double 指针时,值保持不变。

这是我测试过的代码:

int main()
{
  int *p_a = malloc(sizeof(int));
  *p_a = 1;

  char *p_b = malloc(sizeof(char));
  *p_b = 'b';

  float *p_c = malloc(sizeof(float));
  *p_c = 3.565;

  double *p_d = malloc(sizeof(double));
  *p_d = 7.77;

  printf("Before 'free()':\n");
  printf("A: %p\t%d\n", p_a, *p_a);
  printf("B: %p\t%c\n", p_b, *p_b);
  printf("C: %p\t%g\n", p_c, *p_c);
  printf("D: %p\t%g\n", p_d, *p_d);

  free(p_a);
  free(p_b);
  free(p_c);
  free(p_d);

  printf("\nAfter 'free()':\n");
  printf("A: %p\t%d\n", p_a, *p_a);
  printf("B: %p\t%c\n", p_b, *p_b);
  printf("C: %p\t%g\n", p_c, *p_c);
  printf("D: %p\t%g\n", p_d, *p_d);

  return 0;
}

这产生了以下输出:

Before 'free()':
A: 0x8f2e008    1
B: 0x8f2e018    b
C: 0x8f2e028    3.565
D: 0x8f2e038    7.77

After 'free()':
A: 0x8f2e008    0
B: 0x8f2e018
C: 0x8f2e028    1.46175e-33
D: 0x8f2e038    7.77

这正常吗?我对malloc()free() 工作原理的理解是,当您malloc() 一个指针时,您会留出一些内存字节供指针指向。这是放在heap上,无法访问,直到free()d或在计算机上触发内存清理,它才会从heap上脱落,这不受控制用户。因此,当您free() 一个指针时,您基本上摆脱了指针与其指向的地址之间的关联,从而允许稍后使用该内存。

考虑到这一切,为什么double 指针仍然指向相同的值?

现在我想一想,为什么指针在free()s 之后有一个地址?

由于我是C 的初学者,因此非常感谢任何和所有的智慧和知识。感谢您的时间。

编辑:经过进一步测试和干预,我发现如果您输入一个超过 6 位的数字并在最后一位(例如 777.7777)上四舍五入为 double,在它是 free()d 之后,它会丢失它的舍入。

举例说明:

Before 'free()':
D: 0x8f2e038    777.778

After 'free()':
D: 0x8f2e038    777.777

这支持@rodrigo 的假设(在 cmets 中),因为 double 是 8 个字节 - 而 intcharfloat 的 4 个字节 - free() 只修改第一个4 个字节,因此它不会丢失所有数据。

感谢大家的帮助!

【问题讨论】:

  • 这是简单的未定义行为。
  • 我没有在你的代码中看到任何双指针。
  • double *p_d = malloc(sizeof(double)); 是指向 double 值的指针。
  • 是的。 C 是我们所说的“不安全”语言。如果您尝试访问不再属于您的内存,它不保证您会找到什么。所有free() 需要做的就是告诉操作系统该内存可用于其他用途。如果您在 free() 之后尝试访问内存,那不是任何人的问题,而是您的问题。
  • 无论如何,知道free() 之后发生的事情实际上并不重要,这让人感到一种安慰。知道那些年轻、冒险的字节在他们面前充满了随机性,我可以高枕无忧了。也许double 更喜欢久坐不动的生活方式。 :P

标签: c pointers memory-management double free


【解决方案1】:

我对 malloc() 和 free() 工作原理的理解是,当你 malloc() 一个指针时,你会留出一些内存字节供指针指向。

你不会 malloc 指针。你 malloc 一块内存。 malloc 返回的值是指向该内存的指针。所以让我们重新表述一下。

我对 malloc() 和 free() 工作原理的理解是,当你 malloc() 一块内存时,你会留出一些内存字节;返回的指针指向该内存块的开头。

正确。

这个[内存块]放在堆上

正确;将堆视为长期随机访问存储。

无法访问的地方

这要么是错误的,要么是荒谬的。 what 不能访问的地方,以及由谁访问?

它[内存块]在空闲之前不会从堆中取出()d

由 malloc 分配的内存块保留供您的程序使用,直到调用 free。要释放块,您调用 free 并传递最初从 malloc 返回的指针,是的。

或在计算机上触发内存清理,不受用户控制

这又是不连贯的。现代操作系统使用虚拟内存。当一个进程被销毁时,整个虚拟地址空间就消失了。

当你 free() 一个指针时,你基本上摆脱了指针和它指向的地址之间的关联,允许以后使用该内存。

不不不不不。这就是你误会的症结所在。

您通过将最初由 malloc 返回的指针传递给 free 来释放一块内存。这会释放内存块,然后允许 malloc 再次分发它,如果您的进程中的其他代码需要内存。

但是 free 不会改变指针指向的地址!指针继续指向一个地址;该地址现在无效!取消引用指向无效内存的指针在 C 中称为“未定义的行为”,实际上任何事情都可能发生。您的程序可能会崩溃。您可以找回曾经存在的价值。您的密码可以通过电子邮件发送给北极秘密巢穴中的黑客。 任何事情都有可能发生,所以不要那样做

(巧合的是,我今天早些时候写了一篇关于这个主题的博客文章,将于周三发布,请关注我的博客了解详情。)

鉴于所有这些,为什么双指针仍然指向相同的值?

未定义的行为意味着任何事情都可能发生。仍然指向相同值的指针是正在发生的事情,正在发生的事情是发生的事情的子集。

现在我想一想,为什么指针在 free()s 之后有一个地址?

指针存储在变量中。 您没有更改变量的值。变量保持其值,直到您更改它们。

我在哪里可以阅读更多内容?

您可能会对这个高度相关的问题感兴趣。

Can a local variable's memory be accessed outside its scope?

该问题与您的问题相同,除了在给定问题中指针指向堆栈而不是堆。但是逻辑是相同的:取消引用指向弹出堆栈帧的指针是未定义的行为,就像取消引用指向已释放块的指针是未定义的行为一样。任何事情都有可能发生。

【讨论】:

  • 谢谢。这是非常有用的。我确信我现在对malloc()free() 的了解比我目前的课程要求的要多得多。在我的书中,这当然不是一件坏事。 :P
  • 关于where it cannot be accessed位:我的教授告诉我们,堆上的内存本质上是其他程序无法访问的,分配后未能释放内存是内存泄漏的原因,随着时间的推移,越来越多的字节被分配,并且没有一个被从堆中取出。这是错误的,还是只是一个过时的概念?在旧系统上仍然如此吗?
  • 我做了更多的研究,我相信我现在明白了。无论如何,程序完成运行后都会从堆中释放内存,但是对于持续运行的程序,内存泄漏是一个问题。它是否正确?如果是这样,我的教授需要对这个主题做更多的研究,或者只是更好地改写他的课程。
  • 即使提到释放指针的值也是未定义的行为。硬件可以检查地址寄存器并在超出范围时捕获。
  • @TheQZ:一个进程中的地址在另一个进程中没有意义这一事实适用于进程中的所有内存,而不仅仅是堆。回到过去,就像 Windows 3.0 时代一样,每个进程共享相同的内存,表现不佳的进程可能会破坏彼此的内存。在使用虚拟内存的现代操作系统中,每个进程都有自己的虚拟内存空间,两个进程必须使用特殊的技术来共享内存。
【解决方案2】:

基本上,一旦你释放了一个指针,你就不应该再使用它指向的数据了。

malloc 为您分配一些您负责写入和读取的内存块。那块“属于”你,但它只是租金!

释放该块后,它会返回系统。如上所述,使用该数据是未定义的行为:预期结果(好像没有免费)、错误结果、崩溃......

根据 双重问题,尝试反转释放(D 到 A)并看看会发生什么......(无论如何,malloc 不认识你打算在那个空间里放一个 double)。

【讨论】:

  • double 保持不变,无论如何。考虑到未分配内存的不可预测性,我想这并不重要,但这看起来很奇怪。
  • @TheQZ:我猜你的观察有以下解释:在这个malloc 实现中,当内存被释放时,块的第一个字节被覆盖,可能将其标记为空闲,或将其链接到免费列表中。如果 4 字节被覆盖,intfloatchar 值将被破坏,但 8 字节长的 double 值仅在最不重要的一半中被破坏。它们的变化很小,而且由于您只打印了几个小数,所以您没有注意到。
  • 谢谢,这似乎很合乎逻辑。非常感激!这有点困扰我。 :P
猜你喜欢
  • 1970-01-01
  • 2016-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-15
相关资源
最近更新 更多