Stack v Heap
The stack is a special region of a computer's memory that acts as a temporary store for variables created by each function. Every time a function declares a new variable, it is "pushed" onto the stack and then when the function exits, these variables are deleted by releasing the memory allocated back to the system. This means that these variables are local in nature. Stack memory is managed by the system and access is very efficient but the amount of stack memory is limited.
The heap is a region of a computer's memory that is not managed automatically by the system but by the user. This is known as dynamic memory allocation. Dynamic memory allocation enables the program to allocate memory while itβs executing. Variables stores on the heap can be accessed globally and there is far more memory available to the user however access tends to be less efficient and slower
All the functions for handling dynamic memory allocation must include the header file stdlib.h.
The amount of memory available for allocation will depend on the physical system. When a memory allocation function is called, the return value must be checked to ensure that the memory was allocated successfully. If the memory allocation fails the programs must be able to handle the failure.
There are 4 library functions defined for dynamic memory allocation in C. They are malloc(), calloc(), realloc() and free().
The malloc() Function
The malloc() function allocates a block of memory of the specified number of bytes. It returns a pointer of type void which can be cast into a pointer of any form. The prototype is -
void *malloc(size_t size)
size β Is the size of the memory block, in bytes and the return type is a pointer to the allocated memory, or NULL if the request fails.
The following example shows the usage of malloc() function.
#include <stdio.h> #include <stdlib.h> int main () { char *str; /* Initial memory allocation */ str = (char *) malloc(25); strcpy(str, "malloc memory string"); printf("String = %s, Address = %u\n", str, str); /* Reallocating memory */ int *i; i = (int *) malloc(1 ); *i=100; printf("Integer = %i, Address = %u\n", *i, i); free(str); free(i); return(0); }
calloc()
calloc() allocates multiple blocks of memory and initializes them to zero. The prototype is-
void *calloc(size_t nitems,size_t size)
nitems β This is the number of elements to be allocated.
size β This is the size of elements.
#include <stdio.h> #include <stdlib.h> int main () { int *ptr; ptr = (int*)calloc(4, sizeof(int)); if (ptr != NULL) puts("Memory allocation was successful."); else puts("Memory allocation failed."); for(int i=0 ; i < 4 ; i++ ) { ptr[i]=i; } for(int i=0 ; i < 4 ; i++ ) { printf("[%i]", ptr[i]); } free( ptr ); return(0); }
the realloc() Function
If the dynamically allocated memory is insufficient or more than required, you can change the size of the block of memory previously allocated memory using realloc() function. The prototype is-
ptr = realloc(void ptr,size_t size);
The ptr argument is a pointer to the original block of memory. The new size, in bytes, is specified by size.
free() Function
Dynamically allocated memory created with either calloc() or malloc() has to be manually released. The prototype is-
free(ptr);
The free() function releases the memory pointed to by ptr. This memory must have been allocated with malloc(), calloc(), or realloc().
#include <stdio.h> #include <stdlib.h> int main () { char *str; /* Initial memory allocation */ str = (char *) malloc(20); strcpy(str, "malloc memory string"); printf("\n String = %s", str ); /* expand memory */ str = realloc(str, 60); strcpy(str, "expand malloc memory string to a total of 50 characters"); printf("\n String = %s", str); str = realloc(str, 14); /*shrink memory */ strcpy(str, "shrink memory "); printf("\n String = %s", str); free(str); return(0);
}
Manipulating Memory Blocks
memset - The memset() function is used to set all bytes in a block to a specified value. In addition, memset can also be used for copying and moving information from one location to another. The function prototype is
void *memset(void *dest, int c, size_t count);
The parameter dest points to the start of a block of memory to be changed, c is the value to set, and the count is the number of bytes, starting at dest, to be set.
memcpy - The memcpy() function copies a specified number of bytes of data between memory blocks. The function prototype is
void *memcpy(void *dest, void *src, size_t count);
The parameters dest and src point to the destination and source memory blocks, respectively. count specifies the number of bytes to be copied. If the two blocks of point to the same area of memory the function may fail
memmove - The memmove() function copies a specified number of bytes from one memory block to another however unlike memcpy() it can handle overlapping memory blocks. The prototype is
void *memmove(void *dest, void *src, size_t count);
The parameters dest and src point to the destination and source memory blocks, respectively. count specifies the number of bytes to be copied.
#include <stdio.h> #include <string.h> char messageorg[60] = "This is the original message"; char messagenew[60] = ""; int main( void ) { printf("\n%s", messageorg); memset(messageorg, 32, 7); printf("\n%s", messageorg); memmove(messagenew ,messageorg+12, 18); printf("\n%s", messagenew); return 0; }