Before we delve into object-oriented development, we need to make a short digression about working with memory in a C++ program. We will not be able to write any complex program without being able to allocate memory during execution and access it.
In C++, objects can be allocated either statically at compile time or dynamically at runtime by calling functions from the standard library. The main difference in using these methods is their efficiency and flexibility. Static allocation is more efficient because memory allocation occurs before the program is executed, but it is much less flexible because we must know in advance the type and size of the object being allocated. For example, it is not at all easy to post the contents of some text file in a static array of strings: we need to know its size in advance. Tasks that require storing and processing an unknown number of elements typically require dynamic memory allocation.
So far, all our examples have used static memory allocation. Let's say defining the variable ival

Int ival = 1024;

forces the compiler to allocate an area in memory large enough to store a variable of type int, associate the name ival with this area, and place the value 1024 there. All this is done at the compilation stage, before the program is executed.
The ival object has two values ​​associated with it: variable value, 1024 in this case, and the address of the memory area where this value is stored. We can refer to either of these two quantities. When we write:

Int ival2 = ival + 1;

then we access the value contained in the ival variable: we add 1 to it and initialize the ival2 variable with this new value, 1025. How can we access the address where the variable is located?
C++ has a built-in pointer type that is used to store the addresses of objects. To declare a pointer containing the address of the ival variable, we must write:

Int *pint; // pointer to an object of type int

There is also a special operation for taking an address, denoted by the symbol &. Its result is the address of the object. The following statement assigns the pint pointer the address of the ival variable:

Int *pint; pint = // pint gets the value of the ival address

We can access the object whose address contains pint (ival in our case) using the operation dereferencing, also called indirect addressing. This operation is indicated by the symbol *. Here's how to indirectly add one to ival using its address:

*pint = *pint + 1; // implicitly increases ival

This expression does exactly the same thing as

Ival = ival + 1; // explicitly increases ival

There is no real point to this example: using a pointer to indirectly manipulate the ival variable is less efficient and less clear. We have given this example only to give a very basic idea of ​​pointers. In reality, pointers are most often used to manipulate dynamically allocated objects.
The main differences between static and dynamic memory allocation are:

  • static objects are represented by named variables, and actions on these objects are performed directly using their names. Dynamic objects do not have proper names, and actions on them are performed indirectly, using pointers;
  • The compiler automatically allocates and frees memory for static objects. The programmer does not need to worry about this himself. Allocating and freeing memory for dynamic objects is entirely the responsibility of the programmer. This is a rather complex task, and it is easy to make mistakes when solving it. The operators new and delete are used to manipulate dynamically allocated memory.

The new operator has two forms. The first form allocates memory for a single object of a certain type:

Int *pint = new int(1024);

Here, the new operator allocates memory for an unnamed object of type int, initializes it with the value 1024 and returns the address of the created object. This address is used to initialize the pint pointer. All actions on such an unnamed object are performed by dereferencing this pointer, because It is impossible to manipulate a dynamic object explicitly.
The second form of the new operator allocates memory for an array given size, consisting of elements of a certain type:

Int *pia = new int;

In this example, memory is allocated for an array of four int elements. Unfortunately, this form The new operator does not allow you to initialize array elements.
What causes some confusion is that both forms of the new operator return the same pointer, in our example it is a pointer to an integer. Both pint and pia are declared exactly the same, but pint points to a single int object, and pia points to the first element of an array of four int objects.
When a dynamic object is no longer needed, we must explicitly release the memory allocated to it. This is done using the delete operator, which, like new, has two forms - for a single object and for an array:

// freeing a single object delete pint; // freeing the array delete pia;

What happens if we forget to free allocated memory? The memory will be wasted, it will be unused, but it cannot be returned to the system because we do not have a pointer to it. This phenomenon has received a special name memory leak. Eventually the program will crash due to lack of memory (if it runs long enough, of course). A small leak can be difficult to detect, but there are utilities that can help you do this.
Our condensed overview of dynamic memory allocation and pointer usage probably raised more questions than it answered. Section 8.4 will cover the issues involved in detail. However, we couldn't do without this digression since the Array class, which we are going to design in subsequent sections, is based on the use of dynamically allocated memory.

Exercise 2.3

Explain the difference between the four objects:

(a) int ival = 1024; (b) int *pi = (c) int *pi2 = new int(1024); (d) int *pi3 = new int;

Exercise 2.4

What does the following code snippet do? What is the logical fallacy? (Note that the index() operation is correctly applied to the pia pointer. An explanation of this fact can be found in Section 3.9.2.)

Int *pi = new int(10); int *pia = new int;
while (*pi< 10) {
pia[*pi] = *pi; *pi = *pi + 1;
) delete pi; delete pia;

In C++, as in many other languages, memory can be allocated statically (memory is allocated before program execution begins and freed after program completion) or dynamically (memory is allocated and freed during program execution).

Static memory allocation is performed for all global and local variables that have explicit declarations in the program (without the use of pointers). In this case, the memory allocation mechanism is determined by the location of the variable description in the program and the memory class specifier in the description. The type of a variable determines the size of the allocated memory area, but the mechanism for allocating memory does not depend on the type. There are two main mechanisms for static memory allocation.

· Memory for each of the global and static (declared with the static specifier) ​​variables is allocated before the start of program execution in accordance with the type description. From the beginning to the end of program execution, these variables are associated with the memory area allocated for them. Thus, they have a global lifetime, but their scope of visibility is different.

· For local variables declared inside a block and without a specifier static memory is allocated in a different way. Before the program starts executing (when it is loaded), a fairly large memory area is allocated, called stack(sometimes the terms are used program stack or call stack to make a distinction between the stack as an abstract data type). The size of the stack depends on the development environment, for example, in MS Visual C++, by default 1 megabyte is allocated for the stack (this value can be customized). During program execution, when entering a certain block, memory is allocated on the stack for variables localized in the block (in accordance with the description of their type); when exiting the block, this memory is freed. These processes are performed automatically, which is why local variables in C++ are often called automatic.

When a function is called, memory is allocated on the stack for its local variables, parameters (the value or address of the parameter is placed on the stack), the result of the function and saving a return point - the address in the program where you need to return when the function completes. When a function exits, all data associated with it is removed from the stack.

The use of the term “stack” is easy to explain - with the accepted approach to memory allocation and freeing, the variables that are placed last on the stack (these are the variables localized in the deepest nested block) are removed from it first. That is, memory allocation and release occurs according to the LIFO principle (LAST IN – FIRST OUT, last in – first out). This is the principle of how a stack works. We will look at the stack as a dynamic data structure and its possible implementation in the next section.



In many cases, statically allocated memory leads to inefficient use of it (this is especially true for large arrays), since the statically allocated memory area is not always actually filled with data. Therefore, in C++, as in many languages, there are convenient means of dynamically generating variables. The essence of dynamic memory allocation is that memory is allocated (captured) upon request from the program and also freed upon request. In this case, the memory size can be determined by the type of the variable or explicitly specified in the request. Such variables are called dynamic. The ability to create and use dynamic variables is closely related to the pointer mechanism.

Summarizing all of the above, we can imagine the following memory allocation scheme during program execution (Figure 2.1). The location of the areas relative to each other in the figure is quite arbitrary, because The operating system takes care of the details of memory allocation.

Figure 2.1 – memory distribution diagram

To conclude this section, let's touch on one painful problem when working with the stack - the possibility of its overflow (this emergency situation is usually called Stack Overflow). The reason that gave rise to the problem is clear - the limited amount of memory that is allocated for the stack when loading a program. The most likely situations for stack overflow are large local arrays and deep nesting of recursive function calls (usually occurs when programming recursive functions is inaccurately, for example, some terminal branch is forgotten).



In order to better understand the stack overflow problem, we recommend performing this simple experiment. In function main declare an array of integers with say a million elements in size. The program will compile, but when you run it, a stack overflow error will occur. Now add the specifier to the beginning of the array description static(or take the array declaration out of the function main) – the program will work!

There is nothing miraculous about this - it’s just that now the array is not placed on the stack, but in the area of ​​global and static variables. The memory size for this area is determined by the compiler - if the program compiled, then it will run.

However, as a rule, there is no need to declare statically generated arrays of huge sizes in a program. In most cases, dynamically allocating memory for such data will be a more efficient and flexible way.

Very often there are problems of processing data arrays, the dimension of which is unknown in advance. In this case, one of two approaches can be used:

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

To use dynamic memory allocation functions, it is necessary to describe a pointer, which is the starting address for storing array elements.

int *p; // pointer to type int

The starting address of a static array is determined by the compiler at the time of its declaration and cannot be changed.

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

Standard dynamic memory allocation functions

Dynamic memory allocation functions are found in random access memory a continuous section of the required length and return the starting address of this section.

Dynamic memory allocation functions:

void * malloc(ArraySizeInBytes);
void * calloc(Number of Elements, Element SizeInBytes);

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

#include

Since both functions presented have a pointer to void as their return value, an explicit cast of the return type is required.

To determine the size of the array in bytes used as an argument to the malloc() function, the number of elements must be multiplied by the size of one element. Since array elements can be both data simple types, and complex types (for example, structures), to accurately determine the size of an element, in general, it is recommended to use the function

int sizeof(type);


which determines the number of bytes occupied by an element of the specified type.

Memory dynamically allocated using the calloc(), malloc() functions can be freed using the function

free(pointer);

A good rule of thumb in programming is to free dynamically allocated memory when it is no longer in use. However, if dynamically allocated memory is not explicitly freed, it will be freed when the program completes.

Dynamic memory allocation for one-dimensional arrays

The form of accessing array elements using pointers is as follows:

int a, *p; // describe a static array and pointer
int b;
p = a; // assign the starting address of the array to the pointer
... // entering array elements
b = *p; // b = a;
b = *(p+i) // b = a[i];

Example in C: Organizing a dynamic one-dimensional array and entering 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 array
int i, n;
system("chcp 1251" );
system("cls" );
printf( "Enter array size: ");
scanf("%d" , &n);
// Memory allocation
a = (int *)malloc(n * sizeof (int ));
// Entering array elements
for (i = 0; i {
printf("a[%d] = " , i);
scanf("%d" , &a[i]);
}
// Output array elements
for (i = 0; i printf("%d " , a[i]);
free(a);
getchar(); getchar();
return 0;
}


Result of program execution:

Dynamic memory allocation for two-dimensional arrays

Suppose you want to place a matrix containing n rows and m columns in dynamic memory. The two-dimensional matrix will be located in RAM in the form of a tape consisting of row elements. In this case, the index of any element of a two-dimensional matrix can be obtained using the formula

index = i*m+j;

where i is the number of the current line; j - current column number.

Consider a 3x4 matrix (see figure)

The index of the selected element is determined as

index = 1*4+2=6

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

n m (element size)

However, since this declaration does not explicitly indicate to the compiler the number of elements in the row and column of a two-dimensional array, the traditional access to an element by specifying the row index and column index is incorrect:

a[i][j] - incorrect.

Correctly accessing an element using a pointer would look like

*(p+i*m+j) ,
Where

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

Example in C

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 array
int i, j, n, m;
system("chcp 1251" );
system("cls" );
printf( "Enter the number of lines: ");
scanf("%d" , &n);
printf();
scanf("%d" , &m);
// Memory allocation
a = (int *)malloc(n*m * sizeof (int ));
// Entering array elements
for (i = 0; i // loop through lines
{
for (j = 0; j // loop through columns
{
scanf("%d" , (a + i*m + j));
}
}
// Output array elements
for (i = 0; i // loop through lines
{
for (j = 0; j // loop through columns
{
printf("%5d " , *(a + i*m + j));
}
printf("\n" );
}
free(a);
getchar(); getchar();
return 0;
}

Execution result

Another way to dynamically allocate memory for a two-dimensional array is also possible - using an array of pointers. To do this you need:

  • allocate a block of RAM for an array of pointers;
  • allocate blocks of RAM for one-dimensional arrays, which are rows of the desired matrix;
  • write the addresses of the strings to an array of pointers.

Graphically, this method of memory allocation can be represented as follows.


With this method of memory allocation, the compiler is explicitly told the number of rows and the number of columns in the array.
Example in C

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 a pointer to a string of elements
int i, j, n, m;
system("chcp 1251" );
system("cls" );
printf( "Enter the number of lines: ");
scanf("%d" , &n);
printf( "Enter the number of columns: ");
scanf("%d" , &m);
// Allocating memory for pointers to strings
// Entering array elements
for (i = 0; i // loop through lines
{
// Allocating memory for storing strings
a[i] = (int *)malloc(m * sizeof (int ));
for (j = 0; j // loop through columns
{
printf("a[%d][%d] = " , i, j);
scanf("%d" , &a[i][j]);
}
}
// Output array elements
for (i = 0; i< n; i++) // loop through lines
{
for (j = 0; j< m; j++) // loop through columns
{
printf("%5d " , a[i][j]); // 5 acquaintances for an array element
}
printf("\n" );
}
// Clear memory
for (i = 0; i< n; i++) // loop through lines
free(a[i]); // freeing memory for the string
free(a);
getchar(); getchar();
return 0;
}

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

Using dynamic memory allocation for row pointers, you can allocate free arrays. A two-dimensional array (matrix) is called free, the size of the rows of which can be different. The advantage of using a free array is that you don't have to allocate too much computer memory to accommodate a string of the maximum possible length. In fact, a free array is a one-dimensional array of pointers to one-dimensional data arrays.

To place a matrix with rows of different lengths in RAM, you need to introduce an additional array m in which the sizes of the rows will be stored.

Example in C

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 lines: ");
scanf("%d" , &n);
a = (int **)malloc(n * sizeof (int *));
m = (int *)malloc(n * sizeof (int )); // array of the number of elements in the rows of array a
// Entering array elements
for (i = 0; i {
printf( "Enter the number of columns for row %d: ", i);
scanf("%d" , &m[i]);
a[i] = (int *)malloc(m[i] * sizeof (int ));
for (j = 0; j printf("a[%d][%d]= " , i, j);
scanf("%d" , &a[i][j]);
}
}
// Output array elements
for (i = 0; i {
for (j = 0; j {
printf("%3d " , a[i][j]);
}
printf("\n" );
}
// Free memory
for (i = 0; i< n; i++)
{
free(a[i]);
}
free(a);
free(m);
getchar(); getchar();
return 0;
}


Execution result

Memory reallocation

If the size of the allocated memory cannot be set in advance, for example when entering a sequence of values ​​before a certain command, then to increase the size of the array when entering the next value, you must do the following:

  • Allocate a memory block of size n+1 (1 more than the current array size)
  • Copy all values ​​stored in the array to the newly allocated memory area
  • Free up memory previously allocated for storing an array
  • Move the array start pointer to the beginning of the newly allocated memory area
  • Append the array with the last entered value

All of the above actions (except the last one) are performed by the function

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

  • ptr is a pointer to a block of memory previously allocated by the malloc(), calloc() functions, or to move to a new location. If this parameter is NULL, then a new block is allocated and the function returns a pointer to it.
  • size - the new size, in bytes, of the allocated memory block. If size = 0 , the previously allocated memory is freed and the function returns a null pointer, ptr is set to NULL .

The size of the memory block referenced by ptr is changed to size bytes. The memory block can shrink or grow in size. The contents of a memory block are preserved even if the new block is smaller than the old one. But the data that goes beyond the scope of the new block is discarded. If the new memory block is larger than the old one, then the contents of the newly allocated memory will be undefined.
if (i>2) i -= 2;
printf("\n" );
a = (int *)realloc(a, i * sizeof (int )); // decrease the array size by 2
for (int j = 0; j< i; j++)
printf("%d " , a[j]);
getchar(); getchar();
return 0;
}

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 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; 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. 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 #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++