为啥在使用 malloc() 和 free() 后,两个内存位置会发生变化?

Posted

技术标签:

【中文标题】为啥在使用 malloc() 和 free() 后,两个内存位置会发生变化?【英文标题】:Why might it appear that two memory locations are altered after using malloc() & free()?为什么在使用 malloc() 和 free() 后,两个内存位置会发生变化? 【发布时间】:2014-05-17 11:04:24 【问题描述】:

在下面的代码中,我为几个 int 指针分配内存,设置它们的数据,打印数据信息,然后释放它们。然后,我为一个新的 int 指针分配数据并再次打印所有数据。

我观察到的是,相同的数据被写入内存中的新位置以及先前释放的位置之一。我希望它会写入以前释放的位置之一,但为什么它还要写入新位置?

顺便说一句,我正在使用 MS Visual C++ 2010。

代码如下:

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

int main(int argc, char *argv)

    int *ip;
    int *jp;
    int *xp;

    printf("\n   Memory Allocation Test Bench\n")
    printf("----------------------------------\n");

    ip = malloc(sizeof(*ip));
    jp = malloc(sizeof(void *));

    *ip = 10;
    *jp = 20;

    printf("ip Data: %d, Location: %p\n", *ip, &ip);
    printf("jp Data: %d, Location: %p\n", *jp, &jp);

    free(ip);
    free(jp);

    xp = malloc(sizeof(*xp));

    *xp = 40;

    printf("\nAfter freeing all and setting *xp = 40...\n");
    printf("ip Data: %d, Location: %p\n", *ip, &ip);
    printf("jp Data: %d, Location: %p\n", *jp, &jp);
    printf("xp Data: %d, Location: %p\n", *xp, &xp);

    free(xp);

    printf("\nAfter freeing xp...\n");
    printf("ip Data: %d, Location: %p\n", *ip, &ip);
    printf("jp Data: %d, Location: %p\n", *jp, &jp);
    printf("xp Data: %d, Location: %p\n", *xp, &xp);

    printf("\nPress any key to continue... \n");
    getchar();

    return EXIT_SUCCESS;
 // End of Main

这是我得到的输出,标记显示我在说什么:

您可以看到,当 *xp 设置为 40 时,内存中的两个位置似乎发生了变化。什么可能导致这种情况发生?


更新

在了解到尝试使用已释放的指针是未定义的行为后,我明白不必解释输出,因为导致它的操作是未定义。考虑到这一点,并根据这个问题的答案:What happens to memory after free()?,被释放的指针仍然指向内存中的一个位置,它们只是不应该用于访问它。这引发了关于Setting variable to NULL after free()? 的争论,以首先防止这个问题。


谜团解开

非常感谢Matt McNabb 指出 printf 语句没有打印指针指向的内存中的地址,而是打印指针本身的堆栈地址。像这样替换 printf 行:

printf("xp Data: %d, Location: %p\n", *xp, &xp);

这样的行:

printf("xp Data: %d, Location: %p\n", *xp, xp);

生成了这个新的输出,它清楚地表明一切正常。最后一个 malloc() 语句回收了之前释放的内存。而且由于释放的指针仍然技术上指向内存中的一个有效位置,看起来有两个位置同时被更改:

抛开未定义的行为不谈,这种解释至少可以说明发生了什么 - 一个非常简单(和业余)的编码错误。故事的寓意:记下您正在谈论的地址(堆与堆栈),不要尝试使用已释放的指针访问内存。

【问题讨论】:

您的程序中有多个未定义的行为调用。 不存在数据重复。如果您在调试器中遍历代码,单步执行程序集,您可以解释正在发生的事情。但基本事实是,当您释放缓冲区时,不应再次使用它,因为读取不可靠,写入可能会导致严重问题。 而UB之中,逻辑也是错误的。您正在打印的“位置”是局部变量的地址,与从动态分配返回的地址没有任何关系(如前所述,随后释放并取消引用到行程 UB)。在那里传递的值应该是返回的地址,而不是保存这些地址的指针的地址。例如:printf("ip Data: %d, Location: %p\n", *ip, ip); &。 使用freed 的指针是未定义的行为;所有赌注都取消了。 @KurtE.Clothier:您显然不知道“未定义的行为”是对 C 标准的引用,并且一旦您的程序包含 UB,该标准就允许它做任何事情,包括重新格式化硬盘,重新启动计算机等。释放指针后使用指针指向的是 UB。您接受的答案不是一个好答案,因为它忽略了UB。随着您更多地使用 SO,您可能会理解为什么一些老手会这样评论。 【参考方案1】:

没有“两个内存位置被改变”。我猜你说的是输出线:

ip Data: 40, Location: 0012FF60
xp Data: 40, Location: 0012FF3C

但是,您的“位置”不是40 的存储位置;它是指针ipxp 的位置(在堆栈上)。要查看存储40 的位置,请输出xp,而不是&amp;xp

此外,您应该将匹配%p 格式说明符的参数强制转换为(void *)

尝试打印已释放的指针的值是未定义的行为,更不用说取消引用它了(在这种情况下为ip*ip)。要可靠地打印ip,请在free 之前执行此操作。

【讨论】:

在“更新”版本中,您会注意到“匹配”值都位于同一地址,即您多次输出同一地址的内容 您的示例表明,当您free 内存和malloc 更多内存时,新分配的内存可能与之前freed 的内存位于同一位置。

以上是关于为啥在使用 malloc() 和 free() 后,两个内存位置会发生变化?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们要在 free() 释放内存后清空指针? [复制]

C语言中free掉一段空间后为啥还要使用NULL

为啥在malloc中不调用构造函数? [复制]

为啥在malloc中不调用构造函数? [复制]

C语言free释放内存后为啥指针里的值不变?竟然还可以输出

为啥free函数不在释放内存后,将指针置NULL,野指针有啥用