3

当我尝试访问已释放的地址时,程序将正常运行。有什么需要避免的商品吗?像某些命名的函数have_alloca(void *p)可以返回是否p是有效地址。
我知道valgrindwithtool=memcheck可以做到这一点。但我想知道我是否可以从代码中避免它。
这是一个简单的例子:

#include <stdlib.h>

int main(int argc, char *argv[])
{
    int *p = malloc(sizeof(int) * 10);
    free(p);
    *(p + 1) = 100;

    return 0;
}

为什么我可以访问无效地址?并且程序可以在没有任何警告的情况下编译和运行。顺便说一句:Linux。

4

4 回答 4

11

不,您必须自己处理。从技术上讲,从语言标准的角度来看
,写入不属于您的内存位置是未定义的行为。

语言 C 和 C++ 为您提供了访问变量地址并使用它们的灵活性,代价是这些地址包含您的变量/对象拥有的有效值是您的责任。

理由是:

“权力越大责任越大”

所以责任是你自己来承担的。

于 2012-12-29T05:40:20.530 回答
8

p 就像一个正常的变量

函数 free(p) 的调用只是释放了我们分配的内存,但 p 仍然保存地址值。除非你自己重置:

p = 空;

所以请记住以下提示:

  1. 每次使用指针之前,请检查它是否为 NULL 指针。
  2. 每次释放内存后,将指针设置为NULL。
于 2012-12-29T05:55:32.280 回答
2
  • 释放指针后赋值NULL给指针。
  • 访问前检查不NULL
于 2012-12-29T05:59:26.103 回答
1

除了其他回复之外,您可以考虑使用Boehm 的保守垃圾收集器,然后您将使用GC_malloc而不是malloc并且您不会做任何事情free(即使,当绝对确定内存区域变得无用且无法访问时,您也可以明确地GC_free使用它) .

但是C的要点是不允许使用free-d 数据区。这样做是未定义的行为,任何事情都可能发生。

至于“为什么有时您仍然可以访问无效地址”的问题(在free-d 区域内),答案是特定于实现的。实际上,在 Linux 上,从内核获取内存是通过mmap(2)(或有时sbrk(2))完成的,然后通过系统调用完成释放它munmap(2),这些操作在某种程度上是昂贵的操作,并且需要处理多个页面长度(通常为 4Kbytes)。所以malloc实现试图避免做很多mmapmunmap系统调用,所以通常以free不同的方式管理 -d 内存区域,通过组织它们以便它们可以被进一步重用malloc而不做任何mmap系统调用。这说明内存区域仍然存在(即使它在里面malloc内部簿记),您可以访问它。但是这样做会破坏内部不变量,malloc并且以后会发生严重破坏。

顺便说一句,如果地址在地址空间内,则地址是有效的,您可以通过(顺序)读取/proc/1234/maps文件(Linux上具有某些指定文本内容的伪文件)来查询进程1234的地址空间。从进程里面读取/proc/self/maps。尝试cat /proc/self/maps显示运行该命令的进程的地址空间的cat命令以了解更多信息。请参见proc(5)手册页。

尝试使用strace您的简单程序来了解它正在执行的系统调用。

于 2012-12-29T07:55:25.217 回答