免费矩阵,在这种情况下如何免费工作
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. 的最后一个元素设置为
0
。 This 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
,以指向另一个矩阵。
如果释放矩阵的所有行也释放了指向这些行的指针,您可能会遇到 运行 进入分段错误的情况,因为您不知道指向该地址的指针。
说到这里,您有责任跟踪这一点并确保堆不会累积没有指向它的引用的数据。
我有一个由 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. 的最后一个元素设置为
0
。 This is the same as setting it toNULL
. 但是,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
,以指向另一个矩阵。
如果释放矩阵的所有行也释放了指向这些行的指针,您可能会遇到 运行 进入分段错误的情况,因为您不知道指向该地址的指针。
说到这里,您有责任跟踪这一点并确保堆不会累积没有指向它的引用的数据。