Dynamic memory allocation is necessary for efficient use of computer memory. For example, we wrote some kind of program that processes an array. When writing this program, it was necessary to declare an array, that is, give it a fixed size (for example, from 0 to 100 elements). Then this program will not be universal, because it can process an array of no more than 100 elements. What if we need only 20 elements, but space is allocated in memory for 100 elements, because the array declaration was static, and such use of memory is extremely inefficient.

In C++, the new and delete operations are used to dynamically allocate computer memory. The new operation allocates memory from an area of ​​free memory, and the delete operation frees the allocated memory. The allocated memory must be freed after it is used, so the new and delete operations are used in pairs. Even if you do not explicitly release memory, it will be freed by OS resources when the program terminates. I still recommend not to forget about the delete operation.

// example of using the operation new int *ptrvalue = new int; //where ptrvalue is a pointer to an allocated area of ​​memory of type int //new is the operation of allocating free memory for the created object.

The new operation creates an object of the given type, allocates memory for it, and returns a pointer. the right type to this memory location. If memory cannot be allocated, for example, if there are no free areas, then a null pointer is returned, that is, the pointer will return the value 0. Memory allocation is possible for any type of data: int, float,double,char etc.

// example of using the delete operation: delete ptrvalue; // where ptrvalue is a pointer to an allocated memory area of ​​type int // delete is a memory release operation

Let's develop a program in which a dynamic variable will be created.

// new_delete.cpp: Defines the entry point for the console application. #include "stdafx.h" #include << "ptrvalue = "<< *ptrvalue << endl; delete ptrvalue; // freeing memory system("pause"); return 0; }

// code Code::Blocks

// Dev-C++ code

// new_delete.cpp: Defines the entry point for the console application. #include using namespace std; int main(int argc, char* argv) ( int *ptrvalue = new int; // dynamic memory allocation for an object of type int *ptrvalue = 9; // object initialization via a pointer //int *ptrvalue = new int (9); initialization can be performed immediately when declaring a dynamic cout object<< "ptrvalue = "<< *ptrvalue << endl; delete ptrvalue; // freeing memory return 0; )

B line 10 shows a way to declare and initialize a dynamic object with nine; all you need to do is indicate the value in parentheses after the data type. The result of the program is shown in Figure 1.

Ptrvalue = 9 To continue, press any key. . .

Figure 1 - Dynamic variable

Creating Dynamic Arrays

As mentioned earlier, arrays can also be dynamic. Most often, the new and delete operations are used to create dynamic arrays, rather than to create dynamic variables. Let's look at a code fragment for creating a one-dimensional dynamic array.

// declaration of a one-dimensional dynamic array of 10 elements: float *ptrarray = new float ; // where ptrarray is a pointer to an allocated area of ​​memory for an array of real numbers of type float // in square brackets we indicate the size of the array

After the dynamic array has become unnecessary, you need to free the area of ​​​​memory that was allocated for it.

// freeing memory allocated for a one-dimensional dynamic array: delete ptrarray;

After the delete operator, square brackets are placed, which indicate that a section of memory allocated for a one-dimensional array is being released. Let's develop a program in which we will create a one-dimensional dynamic array filled with random numbers.

// new_delete_array.cpp: Defines the entry point for the console application. #include"stdafx.h" #include !} // in the header file // in the header file < 10; count++) ptrarray = (rand() % 10 + 1) / float((rand() % 10 + 1)); //заполнение массива случайными числами с масштабированием от 1 до 10 cout << "array = "; for (int count = 0; count < 10; count++) cout << setprecision(2) << ptrarray << " "; delete ptrarray; // высвобождение памяти cout << endl; system("pause"); return 0; }

// code Code::Blocks

// Dev-C++ code

// new_delete_array.cpp: Defines the entry point for the console application. #include // in the header file contains a prototype of the time() function #include // in the header file contains the setprecision() function prototype #include #include using namespace std; int main(int argc, char* argv) ( srand(time(0)); // generating random numbers float *ptrarray = new float ; // creating a dynamic array of real numbers with ten elements for (int count = 0; count< 10; count++) ptrarray = (rand() % 10 + 1) / float((rand() % 10 + 1)); //заполнение массива случайными числами с масштабированием от 1 до 10 cout << "array = "; for (int count = 0; count < 10; count++) cout << setprecision(2) << ptrarray << " "; delete ptrarray; // высвобождение памяти cout << endl; system("pause"); return 0; }

The created one-dimensional dynamic array is filled with random real numbers obtained using random number generation functions, and the numbers are generated in the range from 1 to 10, the interval is set as follows - rand() % 10 + 1 . To obtain random real numbers, a division operation is performed using an explicit cast to the real type of the denominator - float((rand() % 10 + 1)) . To show only two decimal places we use the setprecision(2) function , the prototype of this function is in the header file . The time(0) function seeds the random number generator with a temporary value, thus reproducing the randomness of the occurrence of numbers (see Figure 2).

Array = 0.8 0.25 0.86 0.5 2.2 10 1.2 0.33 0.89 3.5 Press any key to continue. . .

Figure 2 - Dynamic array in C++

Upon completion of work with the array, it is deleted, thus freeing up the memory allocated for its storage.

We learned how to create and work with one-dimensional dynamic arrays. Now let's look at a piece of code that shows how to declare a two-dimensional dynamic array.

// declaration of a two-dimensional dynamic array of 10 elements: float **ptrarray = new float* ; // two lines in the array for (int count = 0; count< 2; count++) ptrarray = new float ; // и пять столбцов // где ptrarray – массив указателей на выделенный участок памяти под массив вещественных чисел типа float

First, a second-order pointer float **ptrarray is declared, which refers to an array of float* pointers, where the size of the array is two . After which, in the for loop, each line of the array declared in line 2 memory is allocated for five elements. The result is a two-dimensional dynamic array ptrarray. Consider an example of freeing the memory allocated for a two-dimensional dynamic array.

// freeing memory allocated for a two-dimensional dynamic array: for (int count = 0; count< 2; count++) delete ptrarray; // где 2 – количество строк в массиве

Declaring and deleting a two-dimensional dynamic array is done using a loop, as shown above, you need to understand and remember how this is done. Let's develop a program in which we will create a two-dimensional dynamic array.

// new_delete_array2.cpp: Defines the entry point for the console application. #include "stdafx.h" #include #include #include < 2; count++) ptrarray = new float ; // и пять столбцов // заполнение массива for (int count_row = 0; count_row < 2; count_row++) for (int count_column = 0; count_column < 5; count_column++) ptrarray = (rand() % 10 + 1) / float((rand() % 10 + 1)); //заполнение массива случайными числами с масштабированием от 1 до 10 // вывод массива for (int count_row = 0; count_row < 2; count_row++) { for (int count_column = 0; count_column < 5; count_column++) cout << setw(4) <

// code Code::Blocks

// Dev-C++ code

// new_delete_array2.cpp: Defines the entry point for the console application. #include #include #include #include using namespace std; int main(int argc, char* argv) ( srand(time(0)); // generating random numbers // dynamically creating a two-dimensional array of real numbers with ten elements float **ptrarray = new float* ; // two lines in the array for (int count = 0; count< 2; count++) ptrarray = new float ; // и пять столбцов // заполнение массива for (int count_row = 0; count_row < 2; count_row++) for (int count_column = 0; count_column < 5; count_column++) ptrarray = (rand() % 10 + 1) / float((rand() % 10 + 1)); //заполнение массива случайными числами с масштабированием от 1 до 10 // вывод массива for (int count_row = 0; count_row < 2; count_row++) { for (int count_column = 0; count_column < 5; count_column++) cout << setw(4) <

When outputting the array, the setw() function was used; if you remember, it allocates space of a given size for the output data. In our case, there are four positions for each element of the array, this allows us to align numbers of different lengths along columns (see Figure 3).

2.7 10 0.33 3 1.4 6 0.67 0.86 1.2 0.44 Press any key to continue. . .

Figure 3 - Dynamic array in C++

We discovered the possibilities of dynamic memory allocation. What does it mean? This means that with dynamic memory allocation, memory is reserved not at the compilation stage, but at the program execution stage. And this gives us the ability to allocate memory more efficiently, mainly for arrays. With dynamic memory allocation, we do not need to set the size of the array in advance, especially since it is not always known what size the array should be. Next, let's look at how memory can be allocated.

Memory allocation in C (malloc function)

The malloc() function is defined in the stdlib.h header file and is used to initialize pointers with the required amount of memory. The memory is allocated from the RAM sector available for any programs running on the machine. The argument is the number of bytes of memory that needs to be allocated; the function returns a pointer to the allocated block in memory. The malloc() function works just like any other function, nothing new.

Since different data types have different memory requirements, we somehow have to learn how to get the byte size for different data types. For example, we need a section of memory for an array of values ​​of type int - this is one size of memory, and if we need to allocate memory for an array of the same size, but of type char - this is a different size. Therefore, you need to somehow calculate the memory size. This can be done using the sizeof() operation, which takes an expression and returns its size. For example, sizeof(int) will return the number of bytes needed to store an int value. Let's look at an example:

#include int *ptrVar = malloc(sizeof(int));

In this example, in line 3 The ptrVar pointer is assigned an address to a memory location whose size corresponds to the int data type. Automatically, this memory area becomes inaccessible to other programs. This means that after the allocated memory becomes unnecessary, it must be explicitly released. If the memory is not explicitly released, then upon completion of the program, the memory will not be freed for the operating system, this is called a memory leak. You can also determine the size of the allocated memory that needs to be allocated by passing a null pointer, here is an example:

Int *ptrVar = malloc(sizeof(*ptrVar));

What's going on here? The sizeof(*ptrVar) operation will estimate the size of the memory location referenced by the pointer. Since ptrVar is a pointer to an int memory location, sizeof() will return the size of the integer. That is, in fact, based on the first part of the pointer definition, the size for the second part is calculated. So why do we need this? This may be necessary if we suddenly need to change the pointer definition, int , for example, to float and then we do not need to change the data type in two parts of the pointer definition. It will be enough if we change the first part:

Float *ptrVar = malloc(sizeof(*ptrVar));

As you can see, there is one very strong point in this notation, we should not call the malloc() function using sizeof(float) . Instead, we passed a pointer to the float type to malloc(), in which case the size of the allocated memory will automatically determine itself!

This is especially useful if you need to allocate memory far from the pointer definition:

Float *ptrVar; /* . . . one hundred lines of code */ . . . ptrVar = malloc(sizeof(*ptrVar));

If you were to use a memory allocation construct with the sizeof() operation, then you would have to find the definition of a pointer in the code, look at its data type, and only then you would be able to allocate memory correctly.

Freeing allocated memory

Freeing memory is done using the free() function. Here's an example:

Free(ptrVar);

After freeing the memory, it is good practice to reset the pointer to zero, that is, assign *ptrVar = 0 . If you assign 0 to a pointer, the pointer becomes null, in other words, it no longer points to anything. Always assign 0 to the pointer after freeing the memory, otherwise, even after freeing the memory, the pointer still points to it, which means you could accidentally harm other programs that might be using this memory, but you don't even know anything about it you will find out and think that the program works correctly.

P.S.: Anyone who is interested in video editing may be interested in this Windows 7 video editor. The video editor is called Movavi, maybe someone is already familiar with it or has even worked with it. With this program in Russian, you can easily add video from your camera, improve the quality and apply beautiful video effects.

Last update: 05/28/2017

When creating an array with a fixed size, a certain amount of memory is allocated for it. For example, let's say we have an array with five elements:

Double numbers = (1.0, 2.0, 3.0, 4.0, 5.0);

For such an array, the memory allocated is 5 * 8 (double size) = 40 bytes. This way we know exactly how many elements there are in the array and how much memory it takes up. However, this is not always convenient. Sometimes it is necessary that the number of elements and, accordingly, the size of allocated memory for an array be determined dynamically depending on certain conditions. For example, the user can enter the size of the array himself. And in this case, we can use dynamic memory allocation to create the array.

To control dynamic memory allocation, a number of functions are used, which are defined in the stdlib.h header file:

    malloc() . Has a prototype

    Void *malloc(unsigned s);

    Allocates memory of length s bytes and returns a pointer to the beginning of the allocated memory. Returns NULL if unsuccessful

    calloc() . Has a prototype

    Void *calloc(unsigned n, unsigned m);

    Allocates memory for n elements of m bytes each and returns a pointer to the beginning of the allocated memory. Returns NULL if unsuccessful

    realloc() . Has a prototype

    Void *realloc(void *bl, unsigned ns);

    Resizes the previously allocated block of memory pointed to by pointer bl to ns bytes in size. If the bl pointer has a NULL value, that is, no memory was allocated, then the function's action is similar to that of malloc

    free() . Has a prototype

    Void *free(void *bl);

    Frees a previously allocated block of memory, the beginning of which is pointed to by the bl pointer.

    If we do not use this function, the dynamic memory will still be freed automatically when the program exits. However, it is still a good practice to call the free() function, which allows you to free up memory as early as possible.

Let's consider using functions on a simple problem. The length of the array is unknown and is entered during program execution by the user, and also the values ​​of all elements are entered by the user:

#include #include int main(void) ( int *block; // pointer to the memory block int n; // number of array elements // enter the number of elements printf("Size of array="); scanf("%d", &n); / / allocate memory for the array // the malloc function returns a pointer of type void* // which is automatically converted to type int* block = malloc(n * sizeof(int)); // enter numbers into the array for(int i=0;i)

Console output of the program:

Size of array=5 block=23 block=-4 block=0 block=17 block=81 23 -4 0 17 81

Here, a block pointer of type int is defined to manage memory for the array. The number of array elements is unknown in advance; it is represented by the variable n.

First, the user enters the number of elements that fall into the variable n. After this, you need to allocate memory for a given number of elements. To allocate memory here, we could use any of the three functions described above: malloc, calloc, realloc. But specifically in this situation, we will use the malloc function:

Block = malloc(n * sizeof(int));

First of all, it should be noted that all three of the above functions, for the sake of universality of the return value, return a pointer of type void * as a result. But in our case, an array of type int is created, which is manipulated by a pointer of type int * , so the result of the malloc function is implicitly cast to type int * .

The malloc function itself is passed the number of bytes for the allocated block. This number is quite simple to calculate: just multiply the number of elements by the size of one element n * sizeof(int) .

After all actions are completed, the memory is freed using the free() function:

Free(block);

It is important that after executing this function, we will no longer be able to use the array, for example, display its values ​​on the console:

Free(block); for(int i=0;i

And if we try to do this, we will get undefined values.

Instead of the malloc function, we could similarly use the calloc() function, which takes the number of elements and the size of one element:

Block = calloc(n, sizeof(int));

Or you could also use the realloc() function:

Int *block = NULL; block = realloc(block, n * sizeof(int));

When using realloc, it is desirable (in some environments, for example, in Visual Studio, mandatory) to initialize the pointer to at least NULL.

But in general, all three calls in this case would have a similar effect:

Block = malloc(n * sizeof(int)); block = calloc(n, sizeof(int)); block = realloc(block, n * sizeof(int));

Now let's look at a more complex problem - dynamically allocating memory for a two-dimensional array:

#include #include int main(void) ( int **table; // pointer to a memory block for an array of pointers int *rows; // pointer to a memory block for storing row information int rowscount; // number of rows int d; // input number / / enter the number of rows printf("Rows count="); scanf("%d", &rowscount); // allocate memory for a two-dimensional array table = calloc(rowscount, sizeof(int*)); )*rowscount); // loop through rows for (int i = 0; i

The table variable represents a pointer to an array of pointers of type int* . Each table[i] pointer in this array represents a pointer to a subarray of int elements, that is, individual table rows. And the table variable actually represents a pointer to an array of pointers to table rows.

To store the number of elements in each subarray, a rows pointer of type int is defined. It actually stores the number of columns for each row of the table.

First, the number of rows is entered into the rowscount variable. The number of rows is the number of pointers in the array pointed to by the table pointer. And furthermore, the number of rows is the number of elements in the dynamic array pointed to by the rows pointer. Therefore, first you need to allocate memory for all these arrays:

Table = calloc(rowscount, sizeof(int*)); rows = malloc(sizeof(int)*rowscount);

Next in the loop, the number of columns for each row is entered. The entered value goes into the rows array. And in accordance with the entered value, the required memory size is allocated for each line:

Scanf("%d", &rows[i]); table[i] = calloc(rows[i], sizeof(int));

Then the elements for each line are entered.

At the end of the program, memory is released during output. In the program, memory is allocated for table rows, so this memory must be freed:

Free(table[i]);

And in addition, the memory allocated for the table and rows pointers is freed:

Free(table); free(rows);

Console output of the program:

Rows count=2 Columns count for 1=3 table=1 table=2 table=3 Columns count for 2=2 table=4 table=5 1 2 3 4 5

lead time programs. The program allocates memory from the stack space for local variables. However, local variables require a prior determination of the amount of memory allocated for each situation. Although C++ implements such variables efficiently, they require the programmer to know in advance how much memory is needed for each situation.

The second way C++ can store information is by using a dynamic allocation system. In this method, memory is allocated to information from a free memory area as needed. The free memory area is located between the program code with its permanent memory area and the stack (Fig. 24.1). Dynamic allocation is useful when you don't know how many data items will be processed.


Rice. 24.1.

As the program uses the stack area, it increases downwards, that is, the program itself determines the amount of stack memory. For example, a program with a large number recursive functions will take up more stack memory than a program without recursive functions, since local variables and return addresses are stored on stacks. Memory for the program itself and global variables stands out for everything lead time program and is constant for a specific environment.

Memory allocated during program execution is called dynamic. After selection dynamic memory is retained until it is explicitly freed, which can only be done using a special operation or library function.

If dynamic memory is not freed before the end of the program, it is freed automatically when the program ends. However, explicitly freeing memory that has become unnecessary is a sign of good programming style.

During program execution, a section of dynamic memory is available wherever the pointer addressing this section is available. Thus, the following are possible three options for working with dynamic memory, allocated in a certain block (for example, in the body of a non-main function).

  • A pointer (to a location of dynamic memory) is defined as a local automatic memory object. In this case, the allocated memory will not be available when leaving the pointer localization block and must be freed before exiting the block.
  • The pointer is defined as a local static memory object. Dynamic memory allocated once in a block is accessed through a pointer each time the block is reentered. Memory should only be released when it is finished using it.
  • The pointer is a global object with respect to the block. Dynamic memory is available in all blocks where the pointer is "visible". Memory should only be released when it is finished using it.

All variables declared in the program are located in one continuous memory area, which is called data segment. Such variables do not change their size during program execution and are called static. The data segment size may not be sufficient to accommodate large amounts of information. The way out of this situation is to use dynamic memory. Dynamic memory- this is the memory allocated to the program for its operation minus the data segment, the stack, which contains local variables of subroutines and the program body itself.

Pointers are used to work with dynamic memory. With their help, access is made to areas of dynamic memory called dynamic variables. For storage dynamic variables A special area of ​​memory called the heap is allocated.

Dynamic Variables are created using special functions and operations. They exist either until the end of the program, or until the memory allocated for them is freed using special functions or operations. That is, the time of life dynamic variables– from the point of creation to the end of the program or to an explicit freeing up memory.

C++ uses two ways to work with dynamic memory:

  1. using the new and delete operations;
  2. use of the malloc (calloc) family of functions (inherited from C).

Working with dynamic memory using the new and delete operations

In the C++ programming language for dynamic memory allocation there are new and delete operations. These operations are used to allocate and free blocks of memory. The area of ​​memory in which these blocks are located is called free memory.

The new operation allows you to allocate and make available a free area in main memory, the size of which corresponds to the data type identified by the type name.

Syntax:

new TypeName;

new TypeName [Initializer];

The value determined is entered into the selected area initializer, which is an optional element. If successful, new returns the address of the beginning of the allocated memory. If an area of ​​the required size cannot be allocated (there is no memory), then the new operation returns a zero address value (NULL).

The syntax for using the operation is:

Pointer = new TypeName[Initializer];

The new float operation allocates a 4-byte chunk of memory. The new int(15) operation allocates a 4-byte chunk of memory and initializes this chunk with the integer value 15. The syntax for using the new and delete operations involves the use of pointers. Each pointer must be declared in advance:

type *PointerName;

For example:

float *pi; //Declaring a variable pi pi=new float; //Allocating memory for the variable pi * pi = 2.25; //Assigning a value

As a type you can use, for example, standard types int, long, float, double, char.

The new operator is most often used to allocate user-defined data types, such as structures, into memory:

struct Node ( char *Name; int Value; Node *Next ); Node *PNode; //pointer is declared PNode = new Node; //memory is allocated PNode->Name = "Ata"; //values ​​are assigned PNode->Value = 1; PNode->Next = NULL;

An array can be used as a type name in the new operation:

newArrayType

When allocating dynamic memory for an array, its dimensions must be fully specified. For example:

ptr = new int ;//10 elements of type int or 40 bytes ptr = new int ;//wrong, because size not determined

This operation allows you to allocate an area in dynamic memory to accommodate an array of the appropriate type, but does not allow you to initialize it. As a result of execution, the new operation will return a pointer whose value is the address of the first element of the array. For example:

int *n = new int;

The new operation allocates a section of dynamic memory sufficient to accommodate a value of type int and writes the address of the beginning of this section to the variable n. Memory for the variable n itself (of a size sufficient to accommodate the pointer) is allocated at the compilation stage.

Dynamic memory allocation

Main application problems

Null pointer

A null pointer is a pointer that stores a special value used to indicate that a given pointer variable does not refer to (point to) any object. In different programming languages ​​it is represented by different constants.

·In C# and Java languages: null

·In C and C++ languages: 0 or NULL macro. In addition, the C++11 standard introduces a new keyword nullptr to denote a null pointer

·In Pascal and Ruby: nil

·In Component Pascal language:NIL

·In Python: None

Pointers are difficult to manage. It's quite easy to write the wrong value to a pointer, which can cause a hard-to-reproduce bug. For example, you accidentally changed the address of a pointer in memory, or incorrectly allocated memory for information, and then a surprise may await you: another very important variable that is used only inside the program will be overwritten. In this case, it will be difficult for you to reproduce the bug. It will not be easy to understand where exactly the error is. And it will not always be trivial to eliminate it (sometimes you will have to rewrite a significant part of the program).

To solve some problems, there are methods of protection and insurance:

Having studied pointers in the C language, we discovered the possibilities of dynamic memory allocation. What does it mean? This means that with dynamic memory allocation, memory is reserved not at the compilation stage, but at the program execution stage. And this gives us the ability to allocate memory more efficiently, mainly for arrays. With dynamic memory allocation, we do not need to set the size of the array in advance, especially since it is not always known what size the array should be. Next, let's look at how memory can be allocated.

The malloc() function is defined in the stdlib.h header file, it is used to initialize pointers with the required amount of memory. The memory is allocated from the RAM sector available for any programs running on the machine. The argument of the malloc() function is the number of bytes of memory that needs to be allocated; the function returns a pointer to the allocated block in memory. The malloc() function works just like any other function, nothing new.

Since different data types have different memory requirements, we somehow have to learn how to get the byte size for different data types. For example, we need a section of memory for an array of values ​​of type int - this is one size of memory, and if we need to allocate memory for an array of the same size, but of type char - this is a different size. Therefore, you need to somehow calculate the memory size. This can be done using the sizeof() operation, which takes an expression and returns its size. For example, sizeof(int) will return the number of bytes needed to store an int value. Let's look at an example:


Yandex.Direct


#include int *ptrVar = malloc(sizeof(int));

In this example, in line 3 The ptrVar pointer is assigned an address to a memory location whose size corresponds to the int data type. Automatically, this memory area becomes inaccessible to other programs. This means that after the allocated memory becomes unnecessary, it must be explicitly released. If the memory is not explicitly released, then upon completion of the program, the memory will not be freed for the operating system, this is called a memory leak. You can also determine the size of the allocated memory that needs to be allocated by passing a null pointer, here is an example:



As you can see, there is one very strong point in this notation, we should not call the malloc() function using sizeof(float). Instead, we passed a pointer to the float type to malloc(), in which case the size of the allocated memory will automatically determine itself!

This is especially useful if you need to allocate memory far from the pointer definition:


float *ptrVar; /* . . . one hundred lines of code */ . . . ptrVar = malloc(sizeof(*ptrVar));

If you were to use a memory allocation construct with the sizeof() operation, then you would have to find the definition of a pointer in the code, look at its data type, and only then you would be able to allocate memory correctly.


Close