-1

How do I free dynamically allocated memory?

Suppose input (assume it is given by user) is 1000 and now if I allocate memory of 1000 and after this(second time) if user gives input as 500 can I reuse already allocated memory ?

If user now inputs value as say 3000 , how do I go with it ? can I reuse already allocated 1000 blocks of memory and then create another 2000 blocks of memory ? or should I create all 3000 blocks of memory ?

which of these is advisable?

#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;
}
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
bbcbbc1
  • 95
  • 7
  • 1
    You can use `realloc` to make the allocation bigger. `realloc` will attempt to increase the size of the memory. If it can't increase the size of the allocation, it will A) allocate a new memory block B) copy the data to the new memory C) deallocate the old memory. – user3386109 Feb 16 '18 at 05:51
  • 2
    The workflow is `alloc`, then any number of `realloc` calls, then a `free`. – tadman Feb 16 '18 at 05:54
  • first user gives input as 500 ,500 blocks of memory will allocated . now second time when i execute user gives 1000 , so now i need to use already allocated 500 blocks and add 500 blocks of memory , how do i do in my code . and 3rd time say user gives 300 as input from already allocated 1000 blocks of memory i need to reuse the memory. How do i do this safely without any warnings/errors. – bbcbbc1 Feb 16 '18 at 06:13
  • @bbcbbc1 That is why, to avoid these sort of confusions, if not anything else, you should `free()` the allocated memory before program ends. The usage would be much more clear then. – Sourav Ghosh Feb 16 '18 at 06:15

2 Answers2

5

I believe, you need to read about the "lifetime" of allocated memory.

For allocator functions, like malloc() and family, (quoting from C11, chapter §7.22.3, for "Memory management functions")

[...] The lifetime of an allocated object extends from the allocation until the deallocation. [....]

So, once allocated, the returned pointer to the memory remains valid until it is deallocated. There are two ways it can be deallocated

  • Using a call to free() inside the program
  • Once the program terminates.

So, the allocated memory is available, from the point of allocation, to the termination of the program, or the free() call, whichever is earlier.


As it stands, there can be two aspects, let me clarify.

  • Scenario 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
    

    is this is the flow you expect, you can use realloc() to resize the allocated memory size. Once you're done, use free().

  • Scenario 2:

    You allocate memory (size M)
    You use the memory
    You're done using
    

    If this is the case, once you're done, use free().

Note: In both the cases, if the program is run multiple times, there is no connection between or among the allocation happening in each individual invocation. They are independent.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • hi , my requirement--- First user gives input as 500 ,500 blocks of memory will allocated . now second time when i execute user gives 1000 , so now i need to use already allocated 500 blocks and add 500 blocks of memory , how do i do in my code . and 3rd time say user gives 300 as input from already allocated 1000 blocks of memory i need to reuse the memory. How do i do this safely without any warnings/errors. How do i proceed with this? – bbcbbc1 Feb 16 '18 at 06:17
0

When you use dynamically allocated memory, and adjust its size, it is important to keep track of exactly how many elements you have allocated memory for.

I personally like to keep the number of elements in use in variable named used, and the number of elements I have allocated memory for in size. For example, I might create a structure for describing one-dimensional arrays of doubles:

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 }

I like to explicitly initialize my dynamically allocated memory pointers to NULL, and their respective sizes to zero, so that I only need to use realloc(). This works, because realloc(NULL, size) is exactly equivalent to malloc(NULL). I also often utilize the fact that free(NULL) is safe, and does nothing.

I would probably write a couple of helper functions. Perhaps a function that ensures there is room for at_least entries in the array:

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. */
}

I would definitely write a helper function that not only frees the memory used, but also updates the fields to reflect that, so that it is completely safe to call double_array_resize() after freeing:

void double_array_free(double_array *ref)
{
    if (ref) {
        free(ref->data);
        ref->size = 0;
        ref->used = 0;
        ref->data = NULL;
    }
}

Here is how a program might use the above.

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;
}

If I wanted to use the double_array as a stack, I might do

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! */
}

The above double_array_push() reallocates for 100 more doubles, whenever the array runs out of room. However, if you pushed millions of doubles, this would mean tens of thousands of realloc() calls, which is usually considered wasteful. Instead, we usually apply a reallocation policy, that grows the size proportionally to the existing size.

My preferred policy is something like (pseudocode)

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

The LIMIT_1 is typically a small number, the minimum size ever allocated. LIMIT_2 is typically a large number, something like 220 (two million plus change), so that at most LIMIT_2 unused elements are ever allocated. FACTOR is between 1 and 2; many suggest 2, but I prefer 3/2.

The goal of the policy is to keep the number of realloc() calls at an acceptable (unnoticeable) level, while keeping the amount of allocated but unused memory low.

The final note is that you should only try to keep around a dynamically allocated buffer, if you reuse it for the same (or very similar) purpose. If you need an array of a different type, and don't need the earlier one, just free() the earlier one, and malloc() a new one (or let realloc() in the helpers do it). The C library will try to reuse the same memory anyway.

On current desktop machines, something like a hundred or a thousand malloc() or realloc() calls is probably unnoticeable compared to the start-up time of the program. So, it is not that important to minimize the number of those calls. What you want to do, is keep your code easily maintained and adapted, so logical reuse and variable and type names are important.

The most typical case where I reuse a buffer, is when I read text input line by line. I use the POSIX.1 getline() function to do so:

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;
Nominal Animal
  • 38,216
  • 5
  • 59
  • 86