Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Digging C malloc() & free() (http://www.go4expert.com/articles/digging-c-malloc-free-t27272/)

Trinity 2Dec2011 22:23

Digging C malloc() & free()
 
malloc allocates certain amount of memory during program execution and free de-allocates memory previously allocated using a call to functions like malloc, calloc or realloc. Memory leak can occurs when a program allocates memory but does not release it.

Understanding malloc() & free()



During C programming, whenever we define any variable, it is automatically allocated memory as per the datatype specified.

For Example, using a gcc compiler,
Code:

int iv = 10;  // an int is allocated 4 bytes
float fv = 1.5; // a float is allocated 4 bytes
char cv = 'A'; // a char is allocated 1 byte

Even if we need a chunk of memory, we have arrays and structs of fixed sizes. But then, why do need to explicitly allocate memory? There are cases for dynamic memory allocation. That is, when we need to allocate memory as per our run-time needs. Therefore, dynamic memory allocation is done mostly using malloc().

There are other api's as well for dynamic memory allocations like calloc(), realloc(), but lets just focus on malloc here in this article.

What is malloc()



malloc() is a library call provided by standard C library. It is used to allocate memory during a program dynamically and returns the pointer to the newly allocated memory.

About malloc()

  • It allocates the specified bytes of memory on heap.
  • It returns a void pointer which needs a type-cast
  • It requires an explicit call to a free() to free the memory allocated onto the heap.
  • The allocated memory is uninitialized.
Usage Syntax:
Code:

<DataType>* ptr = (<DataType>*) malloc ( num * sizeof (<DataType>);
where num is the number of elements to be stored in the memory of type <DataType>.

About free()



Discussing malloc() is incomplete without free(). free() call frees the memory allocated dynamically through malloc(). Freeing the memory means, it is free on heap to be re-used for other memory allocations.

Usage Syntax
Code:

free(ptr);
where 'ptr' is the pointer as referred in above malloc() usage.

An Example


Code:

    int* ptr = NULL;
    int i, n = 0;
    printf("Enter the number\n");
    scanf("%d", &n);
    ptr = (int*) malloc ( n* sizeof(int));

    for (i = 0; i < n; i++)
    {
        *(ptr + i) = i;
    }
    free(ptr);
    ptr = NULL;

Understanding the implementation



Did you notice that we just provide a pointer to the free() call and it would free the entire memory chunk allocated. A question comes up, how did it come to know how much memory it needs to free?

Howsoever it does, whatever be the implementation, one thing is certain that it would need to store an additional parameter which is the total size.

Lets try to play a bit with memory allocations and free().

For the following code:
Code:

int main()
{
    int* ptr = NULL;
    int i, val = 3;
    ptr = (int*)malloc (val * sizeof (int));

    ptr++;
    for (i = (val - 1); i > 0; i--)
    {
        *(ptr + i) = i;
        printf("%d\t", *(ptr + i));
    }
    free (ptr);
    return 0;
}

Whats the output? Ohh.. Crash!!! and that too in the free() call.

Code:

free(): invalid pointer: 0x0874e00c ***
Actually, the problem was the statement incrementing the pointer. You may think that it was still pointing to a memory dynamically allocated, then what went wrong.

First of all, lets first confirm our suspicion. Comment the suspicious statement and retry running it.

Code:

int main()
{
    int* ptr = NULL;
    int i, val = 3;
    ptr = (int*)malloc (val * sizeof (int));

    //ptr++;
    for (i = (val - 1); i > 0; i--)
    {
        *(ptr + i) = i;
        printf("%d\t", *(ptr + i));
    }
    free (ptr);
    return 0;
}

Yes, it worked fine this time.

So, the problem was that the pointer given to free() should be the same as which was allocated memory. Reason being the same memory address is used through some implementation/algorithm to determine what block and size to free in the heap memory. Now this implementation may vary from compiler to compiler.

Here are a few plausible implementations:
  1. During the malloc() call, allocate a separate additional block to store the metadata including size, just before the actual memory to be allocated. The implementation would be to allocate memory of
    size = <specified no. of bytes> + an extra WORD + guard-bytes
    Store the <specified no. of bytes> in the extra WORD, and return the pointer moving it ahead by WORD.

    The free() call would determine the total memory by moving a WORD behind and then free the whole chunk.
  2. Maintain a Link List for all the new memory allocations along with its size. During free(), determine the size based on the memory address pointed by the pointer.
There might be many other implementations which are out of scope to cover all here.

A General Experimental Observation



Please note, in this section we are just taking observations and no conclusions are intended.

We are using the gcc compiler for all our compilations. So, we assume everything as per what gcc compiler e.g an int is 4 bytes.

In the following source code, we are trying to observe the previous 4 bytes for a few variant malloc()'s.
Code:

    int* pMem1 = NULL;
    int* pMem2 = NULL;
    int* pMem3 = NULL;

    printf("Mem allocation of 4 ints\n");
    pMem1 = (int*)malloc(4 * sizeof(int));
    printf("\n(+)  %d\n", *(pMem1 + 4));
    printf("(-) %d\n", *(pMem1 -1));


    printf("Mem allocation of 6 ints\n");
    pMem2 = (int*)malloc(6 * sizeof(int));
    printf("\n(+) %d\n", *(pMem2 + 6));
    printf("(-) %d\n", *(pMem2 -1));

    printf("Mem allocation of 8 ints\n");
    pMem3 = (int*)malloc(8 * sizeof(int));
    printf("\n(+) %d\n", *(pMem3 + 8));
    printf("(-) %d\n", *(pMem3  -1));


    free(pMem1);
    free(pMem2);
    free(pMem3);
    pMem1 = NULL;
    pMem2 = NULL;
    pMem3 = NULL;

Here is the output:
Code:

(+) 0
(-) 25
Mem allocation of 6 ints

(+) 0
(-) 33
Mem allocation of 8 ints

(+) 0
(-) 41

The value zero after the memory chunk could be the guard byte.

But, did you notice? The value of memory block prior to the actual allocated memory varies as the size of memory allocation varies. Possibly this value is stored by malloc() in direct relation with the size of memory chunk specified.

Now lets try to corrupt this value and see what happens.
Code:

    int* pMem1 = NULL;

    printf("Mem allocation of 4 ints\n");
    pMem1 = (int*)malloc(4 * sizeof(int));
    *(pMem1 - 1) = 1;
    free(pMem1);

Output: A segmentation fault during free().

In another experiment, lets try to corrupt the later value, preferably a negative value for the MSB to be non-zero.
Code:

    int* pMem1 = NULL;
 
    printf("Mem allocation of 4 ints\n");
    pMem1 = (int*)malloc(4 * sizeof(int));
    *(pMem1 + 4) = -12;
    free(pMem1);

output: It works all fine.

Recommendations while using malloc() and free()


  • Every malloc() must be followed by a free() to avoid any memory leak.
  • Its always recommended to assign a NULL to a pointer which is freed.


All times are GMT +5.5. The time now is 23:44.