C memory allocation. Memory allocation

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 memory area 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 of the correct type to the given 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;"pause"); return 0; }

delete ptrvalue; // freeing memory system(

// code Code::Blocks

// Dev-C++ code 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 10 line

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."stdafx.h" #include !} // new_delete_array.cpp: Defines the entry point for the console application. #include // new_delete_array.cpp: Defines the entry point for the console application. #include < 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; }

delete ptrvalue; // freeing memory system(

// code Code::Blocks

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 // in the header file // new_delete_array.cpp: Defines the entry point for the console application. #include contains a prototype of the time() function #include contains the setprecision() function prototype #include #include< 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; }

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 . 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. Let's 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 contains the setprecision() function prototype #include contains the setprecision() function prototype #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) <

delete ptrvalue; // freeing memory system(

// code Code::Blocks

// new_delete_array2.cpp: Defines the entry point for the console application. #include contains the setprecision() function prototype #include contains the setprecision() function prototype #include contains the setprecision() function prototype #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++

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, it is a sign of good programming style to explicitly free memory that has become unnecessary.

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.

Static memory allocated even before the program starts running, at the compilation and assembly stage. Static variables have a fixed address, known before the program is launched and does not change during its operation. Static variables are created and initialized before entering the main function, which begins program execution.

There are two types of static variables:

  • global variables are variables defined out of function, the description of which does not contain the word static . Usually descriptions global variables that include the word extern are placed in header files (h-files). The word extern means that the variable is declared, but not created at this point in the program. Definitions global variables, i.e. descriptions without the word extern are placed in implementation files (c-files or cpp-files). Example: The global variable maxind is declared twice:
    • in the h file using the line

      extern int maxind;

      this declaration reports the presence of such a variable, but does not create that variable!
    • in the cpp file using the line

      int maxind = 1000;

      this is the description creates variable maxind and assigns it the initial value 1000 . Note that the language standard does not require the mandatory assignment of initial values ​​to global variables, but, nevertheless, it is better to always do this, otherwise the variable will contain an unpredictable value (garbage, as programmers say). It is good style to initialize all global variables when they are defined.
    Global variables are so called because they are available anywhere in the program in all its files. Therefore, global variable names must be long enough to avoid accidental names of two different variables. For example, the names x or n are not appropriate for a global variable;
  • static variables- these are variables whose description contains the word static . Typically, static variables are described out of function. Such static variables are in every way similar to global ones, with one exception: the scope of a static variable is limited to the single file within which it is defined - and, moreover, it can only be used after it has been declared, i.e. below in the text. For this reason, declarations of static variables are usually placed at the beginning of the file. Unlike global variables, static variables never are not described in h-files (extern and static modifiers conflict with each other). Tip: Use static variables if you want them to be accessible only to the functions described inside the same file. If possible, do not use global variables in such situations, this will avoid naming conflicts when implementing large projects consisting of hundreds of files.
    • A static variable can also be described inside a function, although usually no one does this. The variable is not located on the stack, but in static memory, i.e. it cannot be used in recursion, and its value is stored between different inputs to the function. The scope of such a variable is limited to the body of the function in which it is defined. Otherwise it is similar to a static or global variable. Note that the static keyword in C is used for two different purposes:
      • as an indication of the memory type: the variable is located in static memory, not on the stack;
      • as a way to limit the scope of a variable to a single file (in the case of describing a variable outside a function).
  • The word static can also appear in the function header. However, it is used only to limit the scope of the function name to a single file. Example:

    static int gcd(int x, int y); // Function prototype. . . static int gcd(int x, int y) ( // Implementation... )

    Tip: Use the static modifier in the function header if you know that the function will only be called within a single file. The word static must appear both in the function prototype description and in the function header when implementing it.

Stack or local memory

Local, or stack, variables are variables described inside a function. Memory for such variables is allocated on the hardware stack, see section 2.3.2. Memory is allocated when entering a function or block and freed when exiting a function or block. In this case, the capture and release of memory occurs almost instantly, because the computer only modifies the register containing the address of the top of the stack.

Local variables can be used in recursion because when a function is re-entered, a new set of local variables is created on the stack without destroying the previous set. For the same reason, local variables are thread-safe in parallel programming (see Section 2.6.2). Programmers call this property of a function re-entrability, from English re-enter able - ability to re-enter. This is a very important quality from the point of view of the reliability and safety of the program! A program that works with static variables does not have this property, so to protect static variables you have to use synchronization mechanisms(see 2.6.2), and the program logic becomes dramatically more complicated. You should always avoid using global and static variables when you can use local ones.

The disadvantages of local variables are an extension of their advantages. Local variables are created when you enter a function and disappear when you exit, so they cannot be used as data shared between multiple functions. In addition, the size of the hardware stack is not infinite; the stack may at one point overflow (for example, during deep recursion), which will lead to a catastrophic termination of the program. Therefore, local variables should not be large. In particular, large arrays cannot be used as local variables.

Dynamic memory or heap

In addition to static and stack memory, there is also a practically unlimited memory resource called dynamic, or a bunch(heap). The program can capture sections of dynamic memory of the required size. After use, the previously captured area of ​​dynamic memory should be freed.

Dynamic memory is allocated space in the process's virtual memory between static memory and the stack. (The virtual memory mechanism was discussed in Section 2.6.) Typically, the stack is located at the highest addresses of virtual memory and grows towards smaller addresses (see Section 2.3). The program and constant data are located in low addresses, static variables are located higher. The space above static variables and below the stack is occupied by dynamic memory:

address memory contents

program code and data,

tamper-proof

...

static variables

programs

dynamic memory

max. address (2 32 -4)

stack

The dynamic memory structure is automatically maintained by the C or C++ language runtime system. Dynamic memory consists of captured and free segments, each of which is preceded by a segment descriptor. When executing a memory capture request, the execution system searches for a free segment of sufficient size and captures a segment of the required length in it. When a memory segment is freed, it is marked as free; if necessary, several consecutive free segments are merged.

In the C language, the standard malloc and free functions are used to acquire and free dynamic memory; descriptions of their prototypes are contained in the standard header file "stdlib.h". (The name malloc is short for memory allocate- "memory capture".) The prototypes of these functions look like this:

void *malloc(size_t n); // Capture a memory area // of n bytes in size void free(void *p); // Free a section of // memory with address p

Here n is the size of the captured area in bytes, size_t is the name of one of the integer types that determine the maximum size of the captured area. The type size_t is specified in the standard header file "stdlib.h" using the typedef operator (see p. 117). This ensures the independence of the C program text from the architecture used. On the 32-bit architecture, the type size_t is defined as an unsigned integer:

typedef unsigned int size_t;

The malloc function returns the address of the allocated memory location, or zero if it fails (when there is no free location large enough). The free function frees a piece of memory with a given address. To set the address, a pointer of the general type void* is used. After calling the malloc function, it must be cast to a pointer to a specific type using the type cast operation, see section 3.4.11. For example, the following example grabs a 4000-byte chunk of heap memory and assigns its address to a pointer to an array of 1000 integers:

int *a; // Pointer to an array of integers. . . a = (int *) malloc(1000 * sizeof(int));

The expression in the malloc function argument is 4000 because the size of the integer sizeof(int) is four bytes. To convert a pointer, a type cast (int *) is used from a generic type pointer to a pointer to an integer.

Example: Printing the first n prime numbers

Let's look at an example using dynamic memory capture. You need to enter an integer n and print the first n prime numbers. (A prime number is a number that has no nontrivial divisors.) We use the following algorithm: we sequentially check all odd numbers, starting with three (we consider two separately). We divide the next number by all prime numbers found in the previous steps of the algorithm and not exceeding the square root of the number being tested. If it is not divisible by any of these prime numbers, then it is itself prime; it is printed and added to the array of found primes.

Since the required number of primes n is unknown before the program starts, it is impossible to create an array to store them in static memory. The solution is to grab space for the array in dynamic memory after entering the number n. Here is the full text of the program:

#include contains the setprecision() function prototype #include contains the setprecision() function prototype #include int main() ( int n; // Required number of primes int k; // Current number of found primes int *a; // Pointer to an array of found primes int p; // Next number to check int r; // Integer part square root of p int i; // Index of the prime divisor bool prime; // Sign of prime printf("Enter the number of primes: "); scanf("%d", &n);<= 0) // Некорректное значение =>return 1; // exit with an error code // Grab memory for an array of prime numbers a = (int *) malloc(n * sizeof(int));< n) { // Проверяем число p на простоту r = (int)(// Целая часть корня sqrt((double) p) + 0.001); i = 0; prime = true; while (i < k && a[i] <= r) { if (p % a[i] == 0) { // p делится на a[i] prime = false; // =>a = 2; k = 1; // Add two to the array printf("%d ", a); // and print it p = 3;

while (k

p is not prime, break; // exit the loop ) ++i; // To the next prime divisor ) if (prime) ( // If we find a prime number, a[k] = p; // then add it to the array ++k; // Increase the number of primes printf("%d ", p ); // Print a prime number if (k % 5 == 0) ( // Go to a new line printf("\n"); // after every five numbers ) ) p += 2; // To the next odd number ) if (k % 5 != 0) ( printf("\n"); // Translate the line ) // Free up dynamic memory free(a);

return 0; )

An example of how this program works:

Enter the number of primes: 50 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229

C++ new and delete operators

C++ uses the new and delete operators to acquire and free dynamic memory. They are part of the C++ language, unlike the malloc and free functions that are part of the C library of standard functions.

Let T be some type of C or C++ language, p is a pointer to an object of type T. Then the new operator is used to grab memory of one element of type T:

T*p; p = new T;

For example, to capture eight bytes for a real number of type double, a fragment is used

double *p; p = new double;

When using new , unlike malloc , there is no need to cast a pointer from type void* to the desired type: the new operator returns a pointer to the type written after the word new . Compare two equivalent snippets in C and C++.

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 contains the setprecision() function prototype #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 contains the setprecision() function prototype #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

So. The third type, the most interesting in this topic for us, is the dynamic type of memory.

How did we work with arrays before? int a How do we work now? We allocate as much as needed:

#include < stdio.h> #include < stdlib.h> int main() (size_t size; // Create a pointer to int // – essentially an empty array. int *list; scanf("%lu" , &size); // Allocate memory for size elements of size int list = (int *)malloc(size * sizeof(int));< size; ++i) { scanf (for (int i = 0 ; i < size; ++i) { printf (for (int i = 0 ; i" %d " , *(list + i));) // *

// Don't forget to clean up after yourself!

free(list); )

Void * malloc(size_t size);

But in general, this is a function that allocates size bytes of uninitialized memory (not zeros, but garbage).

#include < stdio.h> #include < stdlib.h> If the allocation was successful, a pointer to the very first byte of the allocated memory is returned. scanf( If unsuccessful - NULL. Also errno will be equal to ENOMEM (we will look at this wonderful variable later). That is, it would be more correct to write:< size; ++i) { scanf (for (int i = 0 ; i int main () ( size_t size; int *list; scanf (< size; ++i) { printf (for (int i = 0 ; i, &size); // *

list = (int *)malloc(size * sizeof(int));

#include < stdlib.h> if (list == NULL ) ( goto error; ) for (int i = 0 ; i

, list + i);

) for (int i = 0 ; i

    , *(list + i));

    ) free(list);

    return 0 ; error: return 1 ; )

    There is no need to clear a NULL pointer

    int main() (free(NULL);)

    – in the same clang everything will go fine (nothing will be done), but in more exotic cases it may well crash the program.

    Next to malloc and free in mana you can also see:

    void * calloc(size_t count, size_t size);

    Just like malloc will allocate memory for count objects of size bytes. The allocated memory is initialized with zeros.

    void * realloc (void *ptr, size_t size);