0% found this document useful (0 votes)
93 views

C - Memory Management

This document discusses dynamic memory management in C using functions like malloc(), calloc(), realloc(), and free() from the <stdlib.h> header file. It provides examples of allocating memory dynamically using malloc() and calloc(), resizing memory using realloc(), and releasing memory using free(). It also discusses how dynamic memory allocation allows memory to be allocated at runtime based on variable sizes, unlike static arrays which require a fixed size at compile time.

Uploaded by

lalitforeverr
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
93 views

C - Memory Management

This document discusses dynamic memory management in C using functions like malloc(), calloc(), realloc(), and free() from the <stdlib.h> header file. It provides examples of allocating memory dynamically using malloc() and calloc(), resizing memory using realloc(), and releasing memory using free(). It also discusses how dynamic memory allocation allows memory to be allocated at runtime based on variable sizes, unlike static arrays which require a fixed size at compile time.

Uploaded by

lalitforeverr
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 5

C - Memory Management

This chapter will explain dynamic memory management in C. The C programming language provides several functions for memory allocation and management. These functions can be found in the <stdlib.h> header file. S.N. 1 2 3 4 Function and Description void *calloc(int num, int size); This function allocates an array of num elements each of which size in bytes will be size. void free(void *address); This function release a block of memory block specified by address. void *malloc(int num); This function allocates an array of num bytes and leave them initialized. void *realloc(void *address, int newsize); This function re-allocates memory extending it upto newsize.

Allocating Memory Dynamically


While doing programming, if you are aware about the size of an array, then it is easy and you can define it as an array. For example to store a name of any person, it can go max 100 characters so you can define something as follows: char name[100]; But now let us consider a situation where you have no idea about the length of the text you need to store, for example you want to store a detailed description about a topic. Here we need to define a pointer to character without defining how much memory is required and later based on requirement we can allocate memory as shown in the below example: #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char name[100]; char *description; strcpy(name, "Zara Ali"); /* allocate memory dynamically */ description =(char *) malloc( 200 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcpy( description, "Zara ali a DPS student in class 10th"); } printf("Name = %s\n", name ); printf("Description: %s\n", description ); } When the above code is compiled and executed, it produces the following result. Name = Zara Ali Description: Zara ali a DPS student in class 10th Same program can be written using calloc() only thing you need to replace malloc with calloc as follows: calloc(200, sizeof(char)); So you have complete control and you can pass any size value while allocating memory unlike arrays where once you defined the size can not be changed.

Resizing and Releasing Memory


When your program comes out, operating system automatically release all the memory allocated by your program but as a good practice when you are not in need of memory anymore then you should release that memory by calling the function free(). Alternatively, you can increase or decrease the size of an allocated memory block by calling the function realloc(). Let us check the above program once again and make use of realloc() and free() functions: #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char name[100]; char *description; strcpy(name, "Zara Ali"); /* allocate memory dynamically */ description ==(char *) malloc( 50 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcpy( description, "Zara ali a DPS student."); } /* suppose you want to store bigger description */ description = realloc( description, 100 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcat( description, "She is in class 10th"); } printf("Name = %s\n", name ); printf("Description: %s\n", description ); /* release memory using free() function */ free(description); } When the above code is compiled and executed, it produces the following result. Name = Zara Ali Description: Zara ali a DPS student.She is in class 10th You can try above example without re-allocating extra memory and strcat() function will give an error due to lack of available memory in description.

Dynamic Memory
Until now, in all our programs, we have only had as much memory available as we declared for our variables, having the size of all of them to be determined in the source code, before the execution of the program. But, what if we need a variable amount of memory that can only be determined during runtime? For example, in the case that we need some user input to determine the necessary amount of memory space. The answer is dynamic memory, for which C++ integrates the operators new and delete.

Operators new and new[]


In order to request dynamic memory we use the operator new. new is followed by a data type specifier and -if a sequence of more than one element is required- the number of these within brackets []. It returns a pointer to the beginning of the new block of memory allocated. Its form is: pointer = new type pointer = new type [number_of_elements] The first expression is used to allocate memory to contain one single element of type type. The second one is used to assign a block (an array) of elements of type type, where number_of_elements is an integer value representing the amount of these. For example:

1 int * bobby; 2 bobby = new int [5];

In this case, the system dynamically assigns space for five elements of type int and returns a pointer to the first element of the sequence, which is assigned to bobby. Therefore, now, bobby points to a valid block of memory with space for five elements of type int.

The first element pointed by bobby can be accessed either with the expression bobby[0] or the expression *bobby. Both are equivalent as has been explained in the section about pointers. The second element can be accessed either with bobby[1] or *(bobby+1) and so on... You could be wondering the difference between declaring a normal array and assigning dynamic memory to a pointer, as we have just done. The most important difference is that the size of an array has to be a constant value, which limits its size to what we decide at the moment of designing the program, before its execution, whereas the dynamic memory allocation allows us to assign memory during the execution of the program (runtime) using any variable or constant value as its size. The dynamic memory requested by our program is allocated by the system from the memory heap. However, computer memory is a limited resource, and it can be exhausted. Therefore, it is important to have some mechanism to check if our request to allocate memory was successful or not. C++ provides two standard methods to check if the allocation was successful: One is by handling exceptions. Using this method an exception of type bad_alloc is thrown when the allocation fails. Exceptions are a powerful C++ feature explained later in these tutorials. But for now you should know that if this exception is thrown and it is not handled by a specific handler, the program execution is terminated. This exception method is the default method used by new, and is the one used in a declaration like:

bobby = new int [5];

// if it fails an exception is thrown

The other method is known as nothrow, and what happens when it is used is that when a memory allocation fails, instead of throwing a bad_alloc exception or terminating the program, the pointer returned by new is a null pointer, and the program continues its execution. This method can be specified by using a special object called nothrow, declared in header <new>, as argument fornew:

bobby = new (nothrow) int [5];

In this case, if the allocation of this block of memory failed, the failure could be detected by checking if bobby took a null pointer value:

1 int * bobby; 2 bobby = new (nothrow) int [5]; 3 if (bobby == 0) { 4 // error assigning memory. Take measures. 5 };

This nothrow method requires more work than the exception method, since the value returned has to be checked after each and every memory allocation, but I will use it in our examples due to its simplicity. Anyway this method can become tedious for larger projects, where the exception method is generally preferred. The exception method will be explained in detail later in this tutorial.

Operators delete and delete[]


Since the necessity of dynamic memory is usually limited to specific moments within a program, once it is no longer needed it should be freed so that the memory becomes available again for other requests of dynamic memory. This is the purpose of the operator delete, whose format is:

1 delete pointer; 2 delete [] pointer;

The first expression should be used to delete memory allocated for a single element, and the second one for memory allocated for arrays of elements. The value passed as argument to delete must be either a pointer to a memory block previously allocated with new, or a null pointer (in the case of a null pointer, delete produces no effect).

// rememb-o-matic #include <iostream> #include <new> using namespace std; int main () { int i,n; int * p; cout << "How many numbers would you like to type? "; cin >> i; p= new (nothrow) int[i]; if (p == 0) cout << "Error: memory could not be allocated"; else {

How many numbers would you like to type? 5 Enter number : 75 Enter number : 436 Enter number : 1067 Enter number : 8 Enter number : 32 You have entered: 75, 436, 1067, 8, 32,

for (n=0; n<i; n++) { cout << "Enter number: "; cin >> p[n]; } cout << "You have entered: "; for (n=0; n<i; n++) cout << p[n] << ", "; delete[] p; } return 0; }

Notice how the value within brackets in the new statement is a variable value entered by the user (i), not a constant value:

p= new (nothrow) int[i];

But the user could have entered a value for i so big that our system could not handle it. For example, when I tried to give a value of 1 billion to the "How many numbers" question, my system could not allocate that much memory for the program and I got the text message we prepared for this case (Error: memory could not be allocated). Remember that in the case that we tried to allocate the memory without specifying the nothrow parameter in the new expression, an exception would be thrown, which if it's not handled terminates the program. It is a good practice to always check if a dynamic memory block was successfully allocated. Therefore, if you use the nothrow method, you should always check the value of the pointer returned. Otherwise, use the exception method, even if you do not handle the exception. This way, the program will terminate at that point without causing the unexpected results of continuing executing a code that assumes a block of memory to have been allocated when in fact it has not.

You might also like