免费矩阵,在这种情况下如何免费工作

Free a matrix, how free works in this case

我有一个由 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);

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

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

您可以将代码重写为:

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

你malloc 3次:

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

首先释放 ptr 是行不通的,因为您随后会丢失 *(ptr+0) 等的值。因此之后,您将无法释放它们。因此,您必须先释放 *(ptr+0)*(ptr+1),然后再分配它们。但是你不必释放 *(ptr+2),因为你没有 malloc 它。

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

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

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

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

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

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

ptr = (char**)malloc(sizeof(char*) * 3)来说,它可能会在0x30 0x31 0x32分配地址,每个地址存储一个8位的类型。假设你做 *(ptr + 0) = malloc(4)。 malloc 调用将 return 指向四个连续的可以存储字符的位置的指针。假设它 returns 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,以指向另一个矩阵。

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

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