如何释放动态分配的内存?
假设输入(假设它是由用户给出的)是1000,现在如果我分配1000的内存,在此之后(第二次)如果用户输入为500,我可以重用已经分配的内存吗?
如果用户现在输入值为3000,我该如何使用它?我可以重用已分配的1000块内存,然后创建另外2000块内存吗?或者我应该创建所有3000块内存?
哪一个是可取的?
#include <stdio.h>
#include <stdlib.h>
typedef struct a
{
int a;
int b;
}aa;
aa* ptr=NULL;
int main() {
//code
int input=2;
ptr=malloc(sizeof(aa)*input);
for(int i=0;i<input;i++)
{
ptr[i].a=10;
ptr[i].b=20;
}
for(int i=0;i<input;i++)
{
printf("%d %d\n",ptr[i].a,ptr[i].b);
}
return 0;
}
答案 0 :(得分:5)
我相信,你需要阅读有关已分配内存的the "lifetime"。
对于分配器函数,如malloc()
和family,(引自C11
,章节§7.22.3,“内存管理函数”)
[...]分配对象的生命周期从分配延伸 直到解除分配。 [....]
因此,一旦分配,返回指向内存的指针仍然有效,直到它被释放。有两种方法可以解除分配
free()
的调用因此,分配的内存可用,从分配点到程序终止,或free()
调用,以较早者为准。
目前,可以有两个方面,让我澄清一下。
场景1:
You allocate memory (size M)
You use the memory
You want the allocated memory to be re-sized (expanded/ shrinked)
You use some more
You're done using
这是您期望的流程,您可以使用realloc()
来调整分配的内存大小。完成后,请使用free()
。
场景2:
You allocate memory (size M)
You use the memory
You're done using
如果是这种情况,请在完成后使用free()
。
注意:在这两种情况下,如果程序多次运行,则在每次单独调用中发生的分配之间没有任何关联。他们是独立的。
答案 1 :(得分:0)
当您使用动态分配的内存并调整其大小时,重要的是要准确跟踪已为内存分配的元素数量。
我个人喜欢在名为used
的变量中保留使用的元素数量,以及在size
中为已分配内存的元素数量。例如,我可能会创建一个用于描述一维双精度数组的结构:
typedef struct {
size_t size; /* Number of doubles allocated for */
size_t used; /* Number of doubles in use */
double *data; /* Dynamically allocated array */
} double_array;
#define DOUBLE_ARRAY_INIT { 0, 0, NULL }
我喜欢将动态分配的内存指针显式初始化为NULL
,并将它们各自的大小归零,这样我只需要使用realloc()
。这是有效的,因为realloc(NULL, size)
完全等同于malloc(NULL)
。我还经常利用free(NULL)
是安全的,什么都不做的事实。
我可能会写几个辅助函数。也许是一个确保数组中有at_least
条目空间的函数:
void double_array_resize(double_array *ref, size_t at_least)
{
if (ref->size < at_least) {
void *temp;
temp = realloc(ref->data, at_least * sizeof ref->data[0]);
if (!temp) {
fprintf(stderr, "double_array_resize(): Out of memory (%zu doubles).\n", at_least);
exit(EXIT_FAILURE);
}
ref->data = temp;
ref->size = at_least;
}
/* We could also shrink the array if
at_least < ref->size, but usually
this is not needed/useful/desirable. */
}
我肯定会编写一个辅助函数,不仅可以释放使用的内存,还可以更新字段以反映这一点,因此在释放后调用double_array_resize()
是完全安全的:
void double_array_free(double_array *ref)
{
if (ref) {
free(ref->data);
ref->size = 0;
ref->used = 0;
ref->data = NULL;
}
}
以下是程序如何使用上述内容。
int main(void)
{
double_array stuff = DOUBLE_ARRAY_INIT;
/* ... Code and variables omitted ... */
if (some_condition) {
double_array_resize(&stuff, 321);
/* stuff.data[0] through stuff.data[320]
are now accessible (dynamically allocated) */
}
/* ... Code and variables omitted ... */
if (weird_condition) {
/* For some reason, we want to discard the
possibly dynamically allocated buffer */
double_array_free(&stuff);
}
/* ... Code and variables omitted ... */
if (other_condition) {
double_array_resize(&stuff, 48361242);
/* stuff.data[0] through stuff.data[48361241]
are now accessible. */
}
double_array_free(&stuff);
return EXIT_SUCCESS;
}
如果我想将double_array
用作堆栈,我可能会这样做
void double_array_clear(double_array *ref)
{
if (ref)
ref->used = 0;
}
void double_array_push(double_array *ref, const double val)
{
if (ref->used >= ref->size) {
/* Allocate, say, room for 100 more! */
double_array_resize(ref, ref->used + 100);
}
ref->data[ref->used++] = val;
}
double double_array_pop(double_array *ref, const double errorval)
{
if (ref->used > 0)
return ref->data[--ref->used];
else
return errorval; /* Stack was empty! */
}
每当阵列用完房间时,上面的double_array_push()
重新分配100多个双打。但是,如果你推动了数百万的双打,这将意味着数以万计的realloc()
次呼叫,这通常被认为是浪费的。相反,我们通常会应用重新分配策略,它会将的大小与现有大小成比例增长。
我的首选政策类似于(伪代码)
If (elements in use) < LIMIT_1 Then
Resize to LIMIT_1
Else If (elements in use) < LIMIT_2 Then
Resize to (elements in use) * FACTOR
Else
Resize to (elements in use) + LIMIT_2
End If
LIMIT_1
通常是一个小数字,是分配的最小尺寸。 LIMIT_2
通常是一个很大的数字,例如2 20 (200万以上的更改),因此最多只会分配LIMIT_2
个未使用的元素。 FACTOR
介于1和2之间;很多人建议2
,但我更喜欢3/2
。
该策略的目标是将realloc()
个调用的数量保持在可接受(不明显)的水平,同时保持已分配但未使用的内存量较低。
最后要注意的是,如果您将其重用于相同(或非常相似)的目的,那么您应该只尝试保留动态分配的缓冲区。如果您需要一个不同类型的数组,并且不需要更早的数组,只需free()
早期数组,malloc()
新数组(或realloc()
帮手呢)。无论如何,C库将尝试重用相同的内存。
在当前的桌面计算机上,与程序的启动时间相比,类似于一百或一千malloc()
或realloc()
次调用的内容可能无法察觉。因此,最小化这些呼叫的数量并不重要。您想要做的是保持代码易于维护和调整,因此逻辑重用以及变量和类型名称非常重要。
我重用缓冲区的最典型情况是,当我逐行读取文本输入时。我使用POSIX.1 getline()
函数来执行此操作:
char *line = NULL;
size_t size = 0;
ssize_t len; /* Not 'used' in this particular case! :) */
while (1) {
len = getline(&line, &size, stdin);
if (len < 1)
break;
/* Have 'len' chars in 'line'; may contain '\0'! */
}
if (ferror(stdin)) {
fprintf(stderr, "Error reading standard input!\n");
exit(EXIT_FAILURE);
}
/* Since the line buffer is no longer needed, free it. */
free(line);
line = NULL;
size = 0;