Contacts

C Selecting memory for a string. Dynamic memory allocation. How to allocate memory by the NEW operator with the interception of a critical situation in which the memory may not stand out? Exceptional situation Bad_alloc. Example

Working with dynamic memory is often a bottleneck in many algorithms, if not applying special tricks.

In the article, I will consider a couple of such techniques. Examples in the article differ (for example, from) in that the overload of the NEW and DELETE operators is used and due to this, the syntax structures will be minimalistic, and the remake of the program is simple. Also described underwater stones found in the process (of course, the guru, who read the standard from the crust to peel, will not be surprised).

0. Do we need manual memory with memory?

First of all, check how a smart allocator can accelerate work with memory.

We will write simple tests for C ++ and C # (C # is known for an excellent memory manager, which divides the objects for generations, uses different pools for objects different sizes etc.).

Class Node (Public: Node * Next;); // ... for (int i \u003d 0; I< 10000000; i++) { Node* v = new Node(); }

Class Node (Public Node next;) // ... for (int l \u003d 0; l< 10000000; l++) { var v = new Node(); }

Despite the entire "spherical-vacuum" of the example, the difference in time turned out 10 times (62 MS against 650 MS). In addition, C # is completed, and according to the rules of good tone in C ++, dedicated objects need to be removed, which will further increase the separation (up to 2580 MS).

1. Pool objects

The obvious solution is to pick up a large memory block from the OS and split it on equal blocks of size SizeOF (Node), when you allocate the memory, take a block from the pool, when released - return to the pool. The pool is easiest to organize with a single-connected list (stack).

Because it is worth the task of minimal interference in the program, everything that can be done is to add an admixture of Blockalloc to the Node class:
Class Node: Public Blockalloc

First of all, we will need a pool of large blocks (pages), which take away from OS or C-Runtime. It can be organized on top of the functions malloc and free, but for greater efficiency (to skip the extra level of abstraction), we use VirtualAlloc / VirtualFree. These functions allocate memory blocks, multiple 4K, as well as reserve the address space of the process by blocks, multiple 64K. At the same time, specifying the Commit and Reserve options, we jump another level of abstraction, reserving the address space and highlighting the memory pages with one call.

PagePool

inline Size_t Align (size_t x, size_t a) (Return ((X-1) | (A-1)) + 1;) // # Define Align (x, a) ((((x) -1) | ( (a) -1)) + 1) Template Class PagePool (Public: Void * GetPage () (void * Page \u003d VirtualAlloc (NULL, Pagesize, MEM_COMMIT | MEM_RERSERVE, Page_ReadWrite); pages.push_back (Page); Return Page;) ~ PagePool () (For (Vector :: ITERATOR I \u003d Pages.begin (); I! \u003d pages.end (); ++ i) (VirtualFree (* i, 0, MEM_RELEASE);)) Private: Vector Pages; );

Then organize the pool of the blocks of the specified size

Blockpool class

template. Class Blockpool: PagePool (Public: blockpool (): Head (NULL) (BLOCKSIZE \u003d Align (SizeOf (T), Alignment); count \u003d pagesize / blocksize;) void * allocblock () (// TODO: LOCK (this) if (! Head) Formatnewpage (); void * tmp \u003d head; head \u003d * (void **) head; return tmp;) void freeblock (void * TMP) (// TODO: LOCK (this) * (void **) TMP \u003d Head; Head \u003d TMP;) Private: void * head; size_t blocksize; size_t count; void formatnewpage () (void * tmp \u003d getpage (); head \u003d tmp; for (size_t i \u003d 0; i< count-1; i++) { void* next = (char*)tmp + BlockSize; *(void**)tmp = next; tmp = next; } *(void**)tmp = NULL; } };

Comment // TODO: LOCK (this) Places are marked, which require interpotional synchronization (for example, use EnterCriticalSection or Boost :: Mutex).

I will explain why when "formatting" the page does not use the freeblock abstraction to add a block into the pool. If something like something like was written

For (size_t i \u003d 0; i< PageSize; i += BlockSize) FreeBlock((char*)tmp+i);

That page on the principle of FIFO would be marked "on the contrary":

Several blocks requested from the pool in a row would have a decreasing address. And the processor does not like to go back, it breaks with prefetch ( Upd.: Not relevant for modern processors). If you make markup in the cycle
For (size_t i \u003d pagesize- (blocksize- (pagesize% blocksize)); i! \u003d 0; i - \u003d blocksize) FreeBlock ...
The markup cycle would go to addresses backwards.

Now that preparations are made, you can describe the grade class.
Template. Class Blockalloc (Public: Static Void * Operator New (SIZE_T S) (if (s! \u003d SizeOf (T)) (Return :: Operator New (S);) Return Pool.Allocblock ();) Static Void Operator Delete (void * M, Size_T S) (if (s! \u003d sizeof (t)) (:: operator delete (m);) ELSE if (m! \u003d NULL) (Pool .... Static Void * Operator New (Size_T, void * M) (Return M;) // ... and the warning about missing placement delete ... static void operator delete (void *, void *) () Private: Static Blockpool Pool; ); Template. Blockpool Blockalloc. :: Pool;

I will explain why you need checks if (s! \u003d SizeOF (T))
When do they work? Then when the class is created / deleted from the base T.
Heirs will use the usual new / delete, but you can also need to mix Blockalloc. Thus, we are easily and safely determined which classes should use the pools, without fearing to break something in the program. Multiple inheritance also works great with this admixture.

Ready. Inheritate Node from Blockalloc and re-conduct a test.
Test time is now - 120 ms. 5 times faster. But in C # allocator is still better. Probably, there is not just a connected list. (If immediately after NEW, immediately call delete, and thus not spend a lot of memory, knowing the data in the cache, we get 62 MS. Strange. Exactly like a U.Net CLR, as if it returns the liberated local variables immediately to the relevant pool, without waiting for gc)

2. Container and its petroleum content

Does it often come across classes that store a lot of different subsidiaries, such that the lifetime of the last not longer than the lifetime of the parent?

For example, it may be an XMLDocument class filled with Node and Attribute classes, as well as C-strings (char *) taken from the text inside the node. Or list of files and directories in file ManagerDownloaded once when rereading the directory and no longer changing.

As was shown in the introduction, Delete is more expensive than New. The idea of \u200b\u200bthe second part of the article is to allocate memory for child objects in a large block associated with the Parent object. When removing the parent object, the subsidiaries will, as usual, are caused by destructors, but the memory will not be returned - it is free to get a large block.

Create a PointerBumpAllocator class, which is able to bite off from a large piece of pieces of different sizes and highlight a new large block when the old will be exhausted.

Pointerbumpallocator class

template. Class PointerbumpLollocator (Public: PointerBumpAllocator (): Free (0) () Void * Allocblock (Size_t Block) (// TODO: LOCK (this) block \u003d align (block, alignment); if (block\u003e free) (free \u003d align (block, pagesize); head \u003d getpage (free);) void * tmp \u003d head; head \u003d (char *) head + block; free - \u003d block; Return TMP;) ~ pointerbumpallocator () (for (vector :: ITERATOR I \u003d Pages.begin (); I! \u003d pages.end (); ++ i) (VirtualFree (* i, 0, Mem_release);)) private: void * getpage (Size_T size) (void * Page \u003d VirtualAlloc (NULL, SIZE, MEM_COMMIT | MEM_RERVE, Page_ReadWrite); pages.push_back (Page) ; Return Page;) Vector pages; Void * Head; Size_t Free; ); Typedef pointerbumpallocator<> DefaultAcator;

Finally, we describe the ChildObject admixture with the overloaded New and Delete, addressed to the specified alloctor:

Template. STRUCT CHILDOBJECT (Return Allocator.AllocBlock (S);) Static Void * Operator New (Return Allocator-\u003e Allocblock (S);) Static void operator delete (void *, size_t) () // * 1 static void operator delete (void *, a *) () Static void operator delete (void *, a &) () Private: Static Void * Operator New (Size_T S ););

In this case, in addition to adding an impurity in the Child class, it will also be necessary to correct all calls to New (or use the Factory Pattern). The NEW operator syntax will be as follows:

New (... Parameters for the operator ...) ChildObject (... Designer Parameters ...)

For convenience, I set two NEW operators receiving A & or A *.
If the allocator is added to the Parent class as a member, more convenient for the first option:
Node \u003d New (alocator) XMLNode (NodeName);
If the allocator is added as ancestor (admixture), it is more convenient for the second:
Node \u003d New (this) XMLNode (NodeName);

A special syntax is not provided for the Delete call, the compiler will cause a standard Delete (marked * 1), regardless of which of the NEW statements was used to create an object. I.e, syntax Delete. normal:
DELETE NODE;

If there is an exception in the childobject designer (or its heir), a delete is called with a signature corresponding to the signature of the NEW operator used when creating this object (the first size_t parameter will be replaced by Void *).

The NEW operator's placement in the Private section protects against the new allest parameter.

I will give a complete example of using the Allocator-Childobject pair:

Example

class Xmldocument: Public DefaultAllocator (Public: ~ XMLDocument () (for (vector :: ITERATOR I \u003d NODES.BEGIN (); I! \u003d nodes.end (); ++ i) (Delete (* i);)) void addnode (char * CONTENT, CHAR * NAME) (char * c \u003d (char *) ALLOCBLOCK (STRLEN (CONTENT) +1); STRCPY (C, CONTENT); char * n \u003d (char *) ALLOCBLOCK (STRLEN (NAME) +1); strcpy (n, content); nodes.push_back (new xmlnode (C, N));) Class XMLNode: Public Childobject (Public: xmlnode (char * _content, char * _name): content (_content), name (_name) () Private: char * content; char * name;); Private: Vector nodes; );

Conclusion. The article was written 1.5 years ago for the sandbox, but alas, did not like the moderator.

    stores global variables and constants;

    the size is determined when compiling.

    Stack (Stack)

    stores local variables, feature arguments and intermediate calculation values;

    the size is determined when the program is started (4 MB is usually allocated).

    Heap (HEAP)

    dynamically distributed memory;

    OS highlights memory for parts (as needed).

Dynamically distributed memory should be used if we are in advance (at the time of writing a program) we do not know how much memory we will need (for example, the size of the array depends on what the user enters during the program) and when working with large data volumes.

Dynamic memory, also called "Pile", is allocated explicitly upon request of the program from resources operating system and controlled by the pointer. It is not initialized automatically and should be clearly released. In contrast to static and automatic memory, dynamic memory is practically not limited (limited only by the size random access memory) and may change during the program

Working with dynamic memory in with

To work with dynamic memory in the language, the following functions are used: malloc, Calloc, Free, Realloc. Consider them in more detail.

    Selection (memory capture): Void * Malloc (Size_T Size);

As an input parameter, the function takes the memory size you want to highlight. The returned value is the pointer to the memory site allocated in a pile. If the OS was not able to allocate the memory (for example, the memory was not enough), then Malloc returns 0.

    After the end of work with the allocated dynamically memory you need to free it. For this purpose, the FREE function is used, which returns the memory for controlled OS: Void FREE (Void * PTR);

If the memory is released before the end of the program, it is exempt automatically upon completion of the program. Nevertheless, the explicit release of unnecessary memory is a sign of a good programming style.

Example: // memory allocation under 1,000 elements of type int

int * p \u003d (int *) malloc (1000 * SizeOF (int));

if (p \u003d\u003d null) cout<< "\n память не выделена";

fREE (P); // Refund of memory into a bunch

2. Allocation (memory capture): Void * Calloc (Size_t NMEmb, Size_t Size);

The function works similarly to malloc, but is characterized by syntax (instead of the size of the memory allocated, you need to specify the number of elements and size of one element) and the fact that the selected memory is reset. For example, after performing int * p \u003d (int *) Calloc (1000, SizeOF (int)) P, P will point to the beginning of an INT type array of 1000 elements initialized with zeros.

3. Changing the size of the memory: void * realloc (void * PTR, SIZE_T SIZE);

The function changes the size of the allocated memory (which indicates pTR, Received from the call malloc, Calloc or Realloc). If the size specified in the parameter size more than the one that was highlighted under the pointer pTR, It is checked if there is an opportunity to highlight the missing cells of memory in a row with already dedicated. If there is not enough space, then a new section of memory is distinguished size and indicator data ptr. Copy to top of a new site.

In the process of executing the program, the dynamic memory site is available everywhere where the pointer addresses this section is available. Thus, the following three options are possible with dynamic memory allocated in a certain unit (for example, in the uncoiling function).

    The pointer (to the dynamic memory site) is defined as a local automatic memory object. In this case, the selected memory is not available when the location block is outputting the location block, and it is necessary to release it before exit from the block.

(int * p \u003d (int *) Calloc (N, SizeOF (Int))

fREE (P); // DIN release. Memory

    The pointer is defined as a local static memory. Dynamic memory allocated once in the block is available through a pointer at each retaining in the block. Memory needs to be released only at the end of its use.

(Static int * p \u003d (int *) Calloc (N, SizeOF (INT));

p \u003d (int *) Calloc (N, SizeOF (INT));

f (50); // Dean allocation. Memory with subsequent release

f1 (100); // Dean allocation. Memory (first appeal)

f1 (100); // Work with Dean. Memory

f1 (0); // DIN release. Memory

    The pointer is a global object with respect to the block. Dynamic memory is available in all blocks where the pointer is "visible". Memory needs to be released only at the end of its use.

int * pg; // Work pointer for Dean. Memory (global variable)

void Init (Int Size)

for (i \u003d 0; i< size; i++) //цикл ввода чисел

(PrintF ("X [% D] \u003d", I);

scanf ("% d", & pg [i]);

iNT SUM (Int Size)

for (i \u003d 0; i< size; i++) //цикл суммирования

// Memory allocation

pg \u003d (int *) Calloc (N, SizeOF (INT));

// Work with Dean Pam

printf (\\ ns \u003d% d \\ n ", sum (n));

fREE (PG); pg \u003d null; // Release of Memory

Work with dynamic memory in C ++

In C ++, there is a mechanism for allocation and release of memory - these are functions. new and delete.Example of use new: int * p \u003d new int; // Selection of memory under 1000 el-order i.e. When using the function new No need to give a pointer and do not need to use sizeOF (). Release allocated by new Memory is carried out by following the following call: Delete P; If you want to select memory for one element, you can use int * q \u003d new int; or int * q \u003d new int (10); // Selected INT is initialized by the value of 10 In this case, the removal will look like this: Delete Q;

lead time programs. Under local variables, the program takes memory from the stack space. However, local variables require a preliminary determination of the amount of memory allocated for each situation. Although C ++ effectively implements such variables, they require a programmer to know in advance what amount of memory is necessary for each situation.

The second way to which C ++ can store information is to use the system of dynamic distribution. In this method, memory is distributed for information from the free area of \u200b\u200bmemory as needed. The free memory area is between the program code with its constant memory area and stack (Fig. 24.1). Dynamic accommodation is convenient when it is unknown how many data items will be processed.


Fig. 24.1.

As the program uses the stack area increases down, that is, the program itself determines the volume of stack memory. For example, a large number program recursive functions will take more stack memory than a program that does not have recursive functionsSince local variables and return addresses are stored in stacks. Memory for the program itself and global variables stand out on everything lead time Programs is constant for a specific environment.

The memory allocated in the process of executing the program is called dynamic. After the selection dynamic It is saved to its explicit release, which can be performed only with a special operation or library function.

If dynamic memory is not released before the end of the program, it is exempt automatically upon completion of the program. Nevertheless, the explicit release of unnecessary memory is a sign of a good programming style.

In the process of executing the program, the dynamic memory site is available everywhere where the pointer addresses this section is available. Thus, the following are possible. three work options with dynamic memorysecreted in a certain unit (for example, in the uncoar body).

  • The pointer (to the dynamic memory site) is defined as a local automatic memory object. In this case, the selected memory is not available when the location block is outputting the location block, and it is necessary to release it before exit from the block.
  • The pointer is defined as a local static memory. Dynamic memory allocated once in the block is available through a pointer at each retaining in the block. Memory needs to be released only at the end of its use.
  • The pointer is a global object with respect to the block. Dynamic memory is available in all blocks where the pointer is "visible". Memory needs to be released only at the end of its use.

All variables declared in the program are placed in one continuous memory area called data segment. Such variables do not change their size during the program execution and are called static. The size of the data segment may not be enough to place large amounts of information. Exit from this situation is to use dynamic memory. Dynamic memory - This is the memory allocated by the program for its work less the data segment, the stack in which local variables of the subroutines and the program itself are located.

Pointers use pointers to work with dynamic memory. With their help, access to the dynamic memory sites that are called dynamic variables. For storage dynamic variables A special memory area is highlighted, called "bugs".

Dynamic variables Created with special functions and operations. They exist either until the end of the program, or until the memory allocated to them is released using special functions or operations. That is, lifetime dynamic variables - from the point of creation to the end of the program or to explicit release memory.

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

  1. use of operations new and delete;
  2. the use of the family of functions Malcos (Calloc) (inherited from C).

Working with dynamic memory using new and delete operations

In the C ++ programming language for dynamic memory distribution There are operations NEW and DELETE. These operations are used to highlight and release memory blocks. The area of \u200b\u200bmemory in which these blocks are placed, called free memory.

The new operation allows you to highlight and make an accessible free plot in the main memory, the dimensions of which correspond to the type of data defined by the type name.

Syntax:

nEW NameType;

nEW NameType [initializer];

The highlighted area is entered by the value determined by initializerwhich is not a mandatory element. In the case of a successful execution, the new returns the address of the start of the selected area of \u200b\u200bmemory. If the section of the desired dimensions cannot be selected (no memory), the new operation returns the zero address value (NULL).

Syntax application operation:

Pointer \u003d NEW nameType [initializer];

The New Float operation highlights the memory area of \u200b\u200b4 bytes. The New INT (15) operation highlights the memory area of \u200b\u200b4 bytes and initializes this site with a whole value 15. The network use syntax and delete involves the use of pointers. Previously each pointer must be announced:

type * name indicator;

For example:

float * PI; // Announcement of the PI PI \u003d New Float variable; // Selecting memory for variable pi * pi \u003d 2.25; // Assignment of value

As such as type, such as standard types can be used. iNT, LONG, FLOAT, DOUBLE, CHAR.

The new operator is most often used to host the type-specific types in the memory of data, for example, structures:

sTRUCT NODE (char * name; int value; node * next); Node * pnode; // Announced PNODE \u003d NEW NODE pointer; // Pnode-\u003e Name \u003d "ATA" is allocated; // Assigns Pnode-\u003e Value \u003d 1 values; PNODE-\u003e NEXT \u003d NULL;

As a name type in NEW operation, an array can be used:

nEW TIMASSIVA

When you allocate dynamic memory for the array, its sizes must be fully defined. For example:

ptr \u003d new int; // 10 elements of type int or 40 bytes ptr \u003d new int; // incorrectly, because Not defined size

Such an operation allows you to highlight the area in dynamic memory to place an array of the corresponding type, but does not allow it to be initialized. As a result of execution, the NEW operation will return the pointer, the value of which is the address of the first element of the array. For example:

int * n \u003d new int;

The NEW operation performs a dynamic memory type as sufficient to place the value of the int, and records the address of the beginning of this area into the variable N. Memory under the variable N (size sufficient to place a pointer) is highlighted at the compilation stage.

Very often there are tasks of processing data arrays, the dimension of which is in advance unknown. In this case, it is possible to use one of two approaches:

  • selection of memory for a static array containing the maximum possible number of elements, but in this case the memory is not consumed rational;
  • dynamic memory allocation for storing data array.

To use dynamic memory allocation functions, it is necessary to describe the pointer, which is the initial address of the storage of the array elements.

int * p; // pointer to type int

The initial address of the static array is determined by the compiler at the time of its announcement and cannot be changed.

For a dynamic array, the initial address is assigned to the declared pointer to the array during the program execution.

Standard dynamic memory allocation functions

Functions of dynamic memory is found in RAM, a continuous portion of the required length and return the initial address of this area.

Dynamic memory distribution functions:

void * malloc (size massivivable);
void * Calloc (numberlements, size elementavabytes);

To use the dynamic memory distribution functions, you need to connect the library. :

#Include.

Since both presented functions as a returned value have a pointer to an empty Void type, an explicit attribution of the type of return value is required.

To determine the size of an array in bytes used as the malloc () function argument, the number of elements is required to multiply by one element. Since the elements of the array can be both these type data and composite types (for example, structures), to accurately determine the size of the element generally recommended the use of the function

int sizeof (type);


which defines the amount of byte occupied by the element of the specified type.

Memory, dynamically highlighted using Calloc (), Malloc () functions, can be released using function

fREE (pointer);

"Common Tone Rule" in programming is the release of dynamically allocated memory in the absence of its further use. However, if dynamically dedicated memory is not exempt, it will be released upon completion of the program execution.

Dynamic memory allocation for one-dimensional arrays

The form of circulation to the elements of the array using the pointers is the following form:

int a, * p; // Describe a static array and pointer
int b;
p \u003d a; // Assign the initial address of the array
... // Entering the elements of the array
b \u003d * p; // B \u003d A;
b \u003d * (P + i) // B \u003d A [i];

Example on C: Organization of a dynamic one-dimensional array and input of its elements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


#Include.
#Include.
#Include.
iNT MAIN ()
{
int * a; // pointer to an array
int i, n;
System ("CHCP 1251");
System ("CLS");
PrintF ( "Enter the size of the array:");
Scanf ("% d", & n);
// Memory allocation
a \u003d (int *) malloc (N * SizeOF (int));
// Entering the elements of the array
for (i \u003d 0; i {
PrintF ("A [% d] \u003d", i);
Scanf ("% d", & a [i]);
}
// Conclusion of the elements of the array
for (i \u003d 0; i PrintF ("% D", A [I]);
FREE (A);
getchar (); getchar ();
return 0;
}


The result of the program:

Dynamic memory allocation for two-dimensional arrays

Let them take place in the dynamic memory a matrix containing N strings and M columns. The two-dimensional matrix will be located in RAM in the shape of a ribbon consisting of elements of strings. In this case, the index of any element of the two-dimensional matrix can be obtained by the formula

index \u003d i * m + j;

where i is the current line number; J is the number of the current column.

Consider the 3x4 matrix (see Fig.)

The index of the dedicated element will be determined as

index \u003d 1 * 4 + 2 \u003d 6

The amount of memory required to accommodate a two-dimensional array is determined as

n · m · (element size)

However, since with this declaration, the compiler clearly does not clearly indicate the number of elements in the line and column of the two-dimensional array, the traditional appeal to the item by specifying the index of the string and the column index is incorrect:

a [i] [J] - incorrect.

The correct appeal to the element using the pointer will look like

* (P + I * M + J),
Where

  • p is an array pointer
  • m - number of columns,
  • i - lines index,
  • j is the column index.

Example on SI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

#Define _CRT_Secure_no_warnings.
#Include.
#Include.
#Include.
iNT MAIN ()
{
int * a; // pointer to an array
int i, j, n, m;
System ("CHCP 1251");
System ("CLS");
PrintF ( "Enter the number of rows:");
Scanf ("% d", & n);
printf ();
Scanf ("% d", & m);
// Memory allocation
a \u003d (int *) malloc (n * m * sizeof (int));
// Entering the elements of the array
for (i \u003d 0; i // Row cycle
{
for (j \u003d 0; j // Cycle on columns
{
Scanf ("% D", (A + I * M + J));
}
}
// Conclusion of the elements of the array
for (i \u003d 0; i // Row cycle
{
for (j \u003d 0; j // Cycle on columns
{
Printf ("% 5d", * (A + I * M + J));
}
PrintF ("\\ n");
}
FREE (A);
getchar (); getchar ();
return 0;
}

Result of execution

Another method of dynamic memory is also possible for a two-dimensional array - using an array of pointers. For this you need:

  • select the RAM unit under the array of pointers;
  • select the blocks of RAM for one-dimensional arrays, which are the rows of the desired matrix;
  • record addresses of rows to the array of pointers.

A graphically such a method of memory allocation can be represented as follows.


With this method, the size of the compiler is clearly indicated by the number of rows and the number of columns in the array.
Example on SI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#Define _CRT_Secure_no_warnings.
#Include.
#Include.
#Include.
iNT MAIN ()
{
int ** a; // pointer to the pointer to the line of elements
int i, j, n, m;
System ("CHCP 1251");
System ("CLS");
PrintF ( "Enter the number of rows:");
Scanf ("% d", & n);
PrintF ( "Enter the number of columns:");
Scanf ("% d", & m);
// Selecting memory for lines
// Entering the elements of the array
for (i \u003d 0; i // Row cycle
{
// Selecting memory for storage strings
a [i] \u003d (int *) malloc (M * SizeOF (int));
for (j \u003d 0; j // Cycle on columns
{
PrintF ("A [% d] [% d] \u003d", i, j);
ScanF ("% d", & a [i] [j]);
}
}
// Conclusion of the elements of the array
for (i \u003d 0; i< n; i++) // Row cycle
{
for (j \u003d 0; j< m; j++) // Cycle on columns
{
PrintF ("% 5D", A [I] [J]); // 5 acquaintance under the element of the array
}
PrintF ("\\ n");
}
// Cleaning Memory
for (i \u003d 0; i< n; i++) // Row cycle
FREE (A [I]); // Liberation of memory for the line
FREE (A);
getchar (); getchar ();
return 0;
}

The result of the program is similar to the previous case.

Using dynamic memory allocation under row pointers, you can place free arrays. A two-dimensional array (matrix) is free, whose string size may be different. The advantage of using a free array is that it is not necessary to remove the computer's memory with a reserve to place the string of the maximum possible length. In fact, the free array is a one-dimensional array of pointers to one-dimensional data arrays.

To accommodate the matrix in the RAM with lines of different lengths, you must enter an additional array M, in which the size of the strings will be stored.

Example on SI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#Define _CRT_Secure_no_warnings.
#Include.
#Include.
iNT MAIN ()
{
int ** a;
int i, j, n, * m;
System ("CHCP 1251");
System ("CLS");
PrintF ( "Enter the number of rows:");
Scanf ("% d", & n);
a \u003d (int **) malloc (N * SizeOF (int *));
m \u003d (int *) malloc (N * SizeOF (int)); // Array of the number of elements in array lines A
// Entering the elements of the array
for (i \u003d 0; i {
PrintF ( "Enter the number of columns% d:", I);
Scanf ("% d", & m [i]);
a [i] \u003d (int *) malloc (m [i] * SizeOF (int));
for (j \u003d 0; j PrintF ("A [% d] [% d] \u003d", i, j);
ScanF ("% d", & a [i] [j]);
}
}
// Conclusion of the elements of the array
for (i \u003d 0; i {
for (j \u003d 0; j {
PrintF ("% 3D", A [I] [J]);
}
PrintF ("\\ n");
}
// Release of Memory
for (i \u003d 0; i< n; i++)
{
FREE (A [I]);
}
FREE (A);
FREE (M);
getchar (); getchar ();
return 0;
}


Result of execution

Redistribution of memory

If the size of the memory of the memory cannot be specified in advance, for example, when entering a sequence of values \u200b\u200bto a specific command, then to increase the size of the array, when you enter the following value, you must perform the following steps:

  • Select the dimension unit of N + 1 (1 more than the current size of the array)
  • Copy all values \u200b\u200bstored in an array in a newly dedicated area of \u200b\u200bmemory.
  • Free memory allocated earlier for the storage of the array
  • Move the pointer to start the array at the beginning of the newly selected memory area.
  • Add an array last introduced value

All the above steps (except last) performs the function

void * realloc (void * PTR, SIZE_T SIZE);

  • pTR is a pointer to a block of previously allocated memory with Malloc (), Calloc () functions or to move to a new place. If this parameter is NULL, the new unit is allocated, and the function returns a pointer to it.
  • size is a new size, in bytes of the memory block allocated. If Size \u003d 0, previously dedicated memory is released and the function returns a zero pointer, the PTR is installed in NULL.

The size of the memory block to which the PTR parameter refers changes to the Size bytes. The memory block can decrease or increase in size. The contents of the memory block are preserved even if the new unit has a smaller size than the old one. But those data that go beyond the new block is discarded. If the new memory block is greater than the old one, the contents of the newly allocated memory will be uncertain.
if (i\u003e 2) i - \u003d 2;
PrintF ("\\ n");
a \u003d (int *) realloc (A, I * SizeOF (int)); // Reducing the size of the array for 2
for (int j \u003d 0; j< i; j++)
PrintF ("% D", A [J]);
getchar (); getchar ();
return 0;
}

The program can store information in the main memory of the computer in two main ways. The first of them uses global and local variables, including arrays, structures and classes. In the case of global and static local variables, the storage location is fixed for all the execution time of the program. In the case of local variables, the memory is highlighted in the stack. Although in Borland C ++, work with these variables is implemented very efficiently, their use requires a programmer to know in advance the size of the memory that will be required during the program execution.

The second method of storing information is the use of a system of dynamic memory of Borland C ++ memory. In this method, memory for storing information is allocated from the free area of \u200b\u200bmemory as needed and returns back, i.e. It is released when the need for it disappeared. The free memory area lies between the memory area where the program and stack is located. This area is called a bunch (HEAP) and is used for requests for dynamic memory allocation.

The advantage of using dynamic memory is that the same memory can be used to store various information in the program execution process. Since the memory is allocated for a specific purpose and is released when its use has been completed, you can use the same memory at another point of time for other purposes in another part of the program. Another advantage of dynamic memory allocation is the ability to create related lists, binary trees and other dynamic data structures.

The core of the dynamic memory allocation of the memory of the language with is the funoc () and free () functions that are parts of the standard library. Whenever the Malloc () function, a memory is received for allocating memory, a portion of available free memory is distinguished. Whenever this memory is released using the FREE () function, this memory returns back the system.

C ++ language defines two dynamic memory allocation operators - New and Delete.

The ANSI C standard defines only four dynamic memory allocation functions: Calloc (), malloc (), free () and realloc (). However, Borland C ++ contains several other dynamic memory allocation functions. When compiling code for a modern 32-bit memory model, memory is flat and usually used only four standard memory allocation functions.

The ANSI C standard defines that the header information required for dynamic memory allocation is contained in the stdlib.h file. However, Borland C ++ allows you to use STDLIB.H or ALLOC.H header files. Here we use the header file stdlib.h, as it provides portability. Some other dynamic memory allocation functions require alloc.h header files, malloc.h or dos.h. It is necessary to pay special attention to which header file is needed to use each function.



Did you like the article? Share it