我有一个矩阵动态分配的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);
问题是它的工作原理是什么,为什么我们不能只释放指针内存或只释放该矩阵的字符串内存?
我相信你考虑内存分配的方式有两个问题。
首先,您似乎认为矩阵被分配在内存中作为可视矩阵。但是您必须记住,编译器没有矩阵的概念。它也不知道您分配的作为起点的内存(在矩阵的一行中(指向该行。
其次,您必须明确地告诉编译器您想要发生什么,而不是依赖编译器来猜测,这也可能导致未定义的行为。
我将使用 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
的地址。
简单地说,假设您将四个整数作为1 2 3 4
存储在int*
中,如果编译器试图释放四个整数作为地址,则会遇到问题,因为您实际上是在告诉编译器free
地址0x01 0x02 0x03 0x4
这些地址在旧系统上会产生可怕的后果,而在现代系统上, 您的应用程序将被终止。
这最初可能看起来很不方便,但是,这意味着使用这些指针具有一定的灵活性。例如,您可能希望再次使用ptr
,而不是调用free
和调用malloc
来指向另一个矩阵。
如果释放矩阵的所有行也释放了指向行的指针,则可能会遇到分段错误的情况,因为您不知道指向此地址的指针。
在这样说时,您有责任跟踪这一点,并确保堆不会累积没有指向它的引用的数据。
您应该始终执行与 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 数组的指针。
- 二.您错误地放置一个包含 4 个字符的数组,并将其分配给您在 A 中创建的指针值之一。
- 三.将 A. 的最后一个元素设置为
0
。这与将其设置为NULL
相同。但是,大卫指出,将其设置为NULL
更好。这是一个特殊值,称为空指针。
首先释放ptr
是行不通的,因为您随后会丢失*(ptr+0)
等的值。因此,您必须首先释放*(ptr+0)
,并在您错误定位它们时*(ptr+1)
。但是你不必释放*(ptr+2)
,因为你没有恶意移动它。
但是,您可以安全地调用 free on*(ptr+2)
,就像您之前将其设置为NULL
一样,并且您始终可以安全地释放 null。为了安全起见,我总是在释放后设置一个指向NULL
的指针。
*(ptr + 0)
和*(ptr + 1)
只是指向您使用(char*)malloc(4);
分配的内容的指针。释放ptr
将释放这些指针本身,而不是它们指向的内存。Free 不是递归的。因此,必须先释放单个指针,然后才能释放指针数组。