【发布时间】: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,您可能会理解为什么一些老手会这样评论。
标签: c pointers visual-c++ memory-management malloc