我有一个基于dinamically分配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);
问题是这样做是什么以及为什么我们不能只释放指针内存或只释放该矩阵的字符串内存?
答案 0 :(得分: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
。
简单地说,假设你有{4}存储在int*
中的四个整数作为1 2 3 4
,如果编译器试图将四个整数作为地址释放,那么你将会遇到问题字面意思告诉编译器free
地址0x01 0x02 0x03 0x4
哪个旧系统会产生可怕的后果,而在现代系统上,你的应用程序将被终止。
这最初可能看起来不方便,但是,这意味着使用这些指针会有一些灵活性。例如,您可能希望再次使用ptr
,而不是拨打free
和拨打malloc
,以指向另一个矩阵。
如果释放矩阵的所有行也释放了指向行的指针,则可能会遇到分段错误,因为您不知道指针指向此地址。
在说这个时,你有责任跟踪这个并确保堆不会累积没有引用它的数据。
答案 1 :(得分:0)
你应该总是使用与mallocs相同数量的frees。
您可以将代码重写为:
char **ptr;
ptr = malloc(sizeof(char*) * 3); //A
*(ptr + 0) = malloc(4); //B
*(ptr + 1) = malloc(4); //B
*(ptr + 2) = 0; //C
你malloc 3次:
0
。 This is the same as setting it to NULL
.但是,大卫指出,将其设置为NULL
会更好。这是一个特殊值,称为空指针。首先释放ptr
将无法正常工作,因为您随后会忽略*(ptr+0)
等的值。因此,您将无法释放它们。因此,您必须首先释放*(ptr+0)
和*(ptr+1)
,因为您要对它们进行malloc。但是你不必释放*(ptr+2)
,因为你没有使用它。
然而,您可以安全地在
*(ptr+2)
上免费拨打,因为您之前已将其设置为NULL
,并且您始终可以安全地免费提供空。为了安全起见,我总是在释放后设置指向NULL
的指针。
*(ptr + 0)
和*(ptr + 1)
只是您使用(char*)malloc(4);
分配的内容的指针。释放ptr
将释放这些指针本身,而不是它们指向的内存。免费不是递归的。所以你必须释放各个指针才能释放指针数组。