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.
During C programming, whenever we define any variable, it is automatically allocated memory as per the datatype specified.
For Example, using a gcc compiler,
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.
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.
where num is the number of elements to be stored in the memory of type <DataType>.
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
where 'ptr' is the pointer as referred in above malloc() usage.
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:
Whats the output? Ohh.. Crash!!! and that too in the free() call.
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.
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:
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.
Here is the output:
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.
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.
output: It works all fine.
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
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.
Code:
<DataType>* ptr = (<DataType>*) malloc ( num * sizeof (<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);
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;
}
Code:
free(): invalid pointer: 0x0874e00c ***
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;
}
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:
- 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. - 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.
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;
Code:
(+) 0 (-) 25 Mem allocation of 6 ints (+) 0 (-) 33 Mem allocation of 8 ints (+) 0 (-) 41
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);
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);
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.