【问题标题】:Free a matrix, how free works in this case释放一个矩阵,在这种情况下 free 是如何工作的
【发布时间】:2018-05-04 05:52:21
【问题描述】:

我有一个动态分配的 char 或其他类似的矩阵:

char **ptr;
ptr = (char**)malloc(sizeof(char*) * 3);
*ptr = (char*)malloc(4);
*(ptr + 1) = (char*)malloc(4);
*(ptr + 2) = 0;

如果我将释放矩阵的行而不释放指针的内存,例如

int i;
for(i = 0; i < 2; i++)
    free(*(ptr + i));

将释放空指针之前最后一行之外的所有行,在这种情况下,仅释放 *ptr 行。 要释放最后一条,我们需要再添加一条指令

free(ptr);

问题是它是如何工作的,为什么我们不能只释放指针内存或只释放该矩阵的字符串内存?

【问题讨论】:

  • ericlippert.com/2014/03/21/find-a-simpler-problem 首先改用一个更简单的概念,即使用一个大小为 3 的指针到 char 的数组。习惯于分配和释放这三个指针。当它起作用时,将数组更改为一块分配的内存 - 如果它对于您的目标确实是必要的。顺便说一下,您可能想多解释一下代码的上下文。它有助于理解您的问题,从而帮助您。
  • 如果我将释放最后一个指针 ptr + 2,它会在没有指令 free(ptr) 的情况下正常工作; ?
  • @Yunnosch,我做了 2 个小时才把问题放在堆栈上,我自己找不到解释
  • 这是对我提议的回答吗?你和我的评论似乎毫无关联......
  • 我说过关于您的链接,我非常了解其中的概念,代码上下文不存在,通常我想知道它是如何工作的以及为什么它会这样工作.

标签: c malloc free heap-memory


【解决方案1】:

我认为您考虑内存分配的方式可能存在两个问题。

首先,您似乎将矩阵视为在内存中分配的可视矩阵。但是您必须记住,编译器没有矩阵的概念。它也不知道你分配的内存作为起点,在矩阵的一行中,指向该行。

其次,你必须明确告诉编译器你想要发生什么,而不是依赖编译器去猜测,这也可能导致未定义的行为。

我将使用 8 位地址来说明这一点。

ptr = (char**)malloc(sizeof(char*) * 3)为例,它可能会在0x30 0x31 0x32分配地址,每个地址将存储一个8位类型。假设您执行*(ptr + 0) = malloc(4)。 malloc 调用将返回一个指针,指向可以存储字符的四个连续位置。假设它返回0x40,这意味着地址0x40 0x41 0x42 0x43 都可以使用。

但是,地址 0x30 处的值被赋值为 0x40,现在请记住,指针不是类型,而是数字的二进制表示,恰好表示地址。因此,指针没有释放器(例如 C++ 中的析构器)的奢侈。编译器不知道该值用于访问分配的内存地址,而是为您存储该值。出于这个原因,调用free(ptr) 只会释放地址0x30 0x31 0x32

简单地说,假设您在int* 中存储了四个整数作为1 2 3 4,如果编译器试图将这四个整数作为地址释放,您将遇到问题,因为您是字面意思告诉编译器 free 地址 0x01 0x02 0x03 0x4 这在旧系统上会产生可怕的后果,而在现代系统上,您的应用程序将被终止。

这最初可能看起来不方便,但是,这意味着使用这些指针有一定的灵活性。例如,您可能希望再次使用ptr,而不是调用free 并再次调用malloc 来指向另一个矩阵。

如果释放矩阵的所有行也释放了指向行的指针,您可能会遇到分段错误,因为您不知道指向该地址的指针。

说到这里,您有责任跟踪这一点并确保堆不会累积没有指向它的引用的数据。

【讨论】:

  • 你解释得很好,我知道在我发布这个问题之前,我的代码有一个未定义的行为,我一直在想这就是它的工作原理。
【解决方案2】:

您应该始终执行与 malloc 完全相同的释放次数。

您可以将代码重写为:

char **ptr;
ptr = malloc(sizeof(char*) * 3); //A
*(ptr + 0) = malloc(4); //B
*(ptr + 1) = malloc(4); //B
*(ptr + 2) = 0; //C

你 malloc 3 次:

  • A.你 malloc 一个数组来保存 3 个指向 char 数组的指针。
  • 乙。您 malloc 一个包含 4 个字符的数组,并将其分配给您在 A 中创建的指针值之一。
  • C.您将 A. 的最后一个元素设置为 0This is the same as setting it to NULL. 但是,大卫指出,将其设置为 NULL 更好。这是一个特殊值,称为空指针。

首先释放ptr 是行不通的,因为你会释放*(ptr+0) 等的值。因此,之后你将无法释放它们。因此,您必须首先释放 *(ptr+0)*(ptr+1),因为您对它们进行了 malloc。但是您不必释放 *(ptr+2),因为您没有对其进行 malloc。

但是,您可以安全地在 *(ptr+2) 上调用 free,因为您之前已将其设置为 NULL,并且您始终可以安全地释放 null。为了安全起见,我总是在释放后设置一个指向NULL 的指针。

*(ptr + 0)*(ptr + 1) 只是指向您使用 (char*)malloc(4); 分配的内容的指针。释放ptr 将释放这些指针本身,而不是它们指向的内存。免费不是递归的。因此,您必须先释放各个指针,然后才能释放指针数组。

【讨论】:

  • 对不起,是我的错,我编辑了,现在你可以看看我冷藏到别的东西了。
  • 你必须小心,你可以将指针设置为0NULL(这是允许的),但技术上它是(void *)0NULL 更干净。 (并使用消失的段落内容——非常谨慎,这对某些人来说更多的是分散注意力而不是好处)
  • 据我所知,NULL 是 (void*)0 的定义,所以是一回事,如果我弄错了,请见谅
猜你喜欢
  • 2020-12-27
  • 1970-01-01
  • 2017-09-27
  • 1970-01-01
  • 2020-08-20
  • 1970-01-01
  • 1970-01-01
  • 2016-03-27
  • 2016-12-05
相关资源
最近更新 更多