多次运行程序时如何使用或释放​​动态分配的内存?

时间:2018-02-16 05:44:28

标签: c pointers dynamic-memory-allocation

如何释放动态分配的内存?

假设输入(假设它是由用户给出的)是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;
}

2 个答案:

答案 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;