gpt4 book ai didi

c++ - 为什么免费调用两次时会崩溃?

转载 作者:IT老高 更新时间:2023-10-28 22:00:00 26 4
gpt4 key购买 nike

在 C 和 C++ 中,free(my_pointer) 在被调用两次时会崩溃。

为什么?每个 malloc 连同大小都有簿记。当第一个 free 被调用时,它会识别出这是分配了什么大小,这就是为什么我们不需要在 free 调用中传递大小。

既然它什么都知道,为什么不检查第二次,什么也不做呢?

要么我不理解 malloc/free 行为,要么 free 没有安全实现。

最佳答案

你不能在未分配的内存上调用 free,标准非常清楚地指出(稍微解释一下,我的重点):

The free function causes the space pointed to by its argument to be deallocated, that is, made available for further allocation. If the argument is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.

例如,如果您要双重释放的地址在一个新 block 的中间被重新分配,而分配它的代码恰好在那里存储了一些看起来像真正的 malloc block 头的东西,会发生什么?喜欢:

 +- New pointer    +- Old pointer
v v
+------------------------------------+
| <Dodgy bit> |
+------------------------------------+

困惑,就是这样。

内存分配功能就像电锯一样是一种工具,只要您正确使用它们,您应该没有问题。然而,如果你滥用它们,后果是你自己的错,要么破坏内存或更糟,要么切断你的一只 ARM :-)


关于评论:

... it can communicate gracefully to enduser about the doubling free the same location.

没有记录所有 mallocfree 调用以确保您不会重复释放 block ,我认为这是可行的。这将需要巨大的开销,并且仍然无法解决所有问题。

如果发生以下情况会发生什么:

  • 线程 A 在地址 42 处分配和释放内存。
  • 线程 B 分配内存地址 42 并开始使用它。
  • 线程 A 再次释放了该内存。
  • 线程 C 分配内存地址 42 并开始使用它。

然后你有线程 B 和 C 都认为他们拥有该内存(这些不一定是执行线程,我在这里使用术语线程作为运行的一段代码 - 它都可以在一个执行线程,但顺序调用)。

不,我认为当前的 mallocfree 只要您正确使用它们就可以了。无论如何都要考虑一下实现你自己的版本,我认为这没有什么问题,但我怀疑你会遇到一些非常棘手的性能问题。


如果您确实想要实现自己的 free 包装器,则可以使其更安全(以牺牲一点性能为代价),特别是使用类似myFreeXxx 调用如下:

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

void myFreeVoid (void **p) { free (*p); *p = NULL; }
void myFreeInt (int **p) { free (*p); *p = NULL; }
void myFreeChar (char **p) { free (*p); *p = NULL; }

int main (void) {
char *x = malloc (1000);
printf ("Before: %p\n", x);
myFreeChar (&x);
printf ("After: %p\n", x);
return 0;
}

代码的结果是,您可以使用指向您的指针的指针调用 myFreeXxx,它会:

  • 释放内存;和
  • 将指针设置为 NULL。

后一点的意思是,如果您再次尝试释放指针,它将什么也不做(因为释放 NULL 已被标准明确涵盖)。

不会保护您免受所有情况的影响,例如,如果您在别处复制指针,释放原件,然后释放拷贝:

char *newptr = oldptr;
myFreeChar (&oldptr); // frees and sets to NULL.
myFreeChar (&newptr); // double-free because it wasn't set to NULL.

如果您打算使用 C11,那么由于 C 具有编译时函数重载,因此有一种比必须为每种类型显式调用不同函数更好的方法。您可以使用泛型选择来调用正确的函数,同时仍然允许类型安全:

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

void myFreeVoid (void **p) { free (*p); *p = NULL; }
void myFreeInt (int **p) { free (*p); *p = NULL; }
void myFreeChar (char **p) { free (*p); *p = NULL; }
#define myFree(x) _Generic((x), \
int** : myFreeInt, \
char**: myFreeChar, \
default: myFreeVoid )(x)

int main (void) {
char *x = malloc (1000);
printf ("Before: %p\n", x);
myFree (&x);
printf ("After: %p\n", x);
return 0;
}

这样,您只需调用 myFree,它就会根据类型选择正确的函数。

关于c++ - 为什么免费调用两次时会崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3117615/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com