• Nem Talált Eredményt

Dynamically allocated arrays

7. Arrays and strings

7.2. Dynamically allocated arrays

A big quantity of data can be stored and processed easily and effectively with the help of arrays. However, the big disadvantage of traditional arrays is that their size is fixed during compilation and it is only during execution that we learn if too much memory was allocated. It is especially true for local arrays created within functions.

It is a general rule that only arrays of little size can be defined within functions. If a bigger quantity of data has to be stored, the methods for dynamic memory allocation should be used since there is much more space in the heap than in stack memory.

We should not forget that memory space can be allocated by the new operator not only for one but also for more elements standing one after the other. In that case, the allocated memory space can be freed by the delete[]

operator.

Methods for dynamic memory allocation and deallocation are present in the form of functions in the Standard Library of C/C++. To access these functions, the cstdlib header file should be included in the source code. The malloc () function allocates a memory space of a given byte size and returns a pointer to the beginning of the block. The calloc () works in a similar way; however, the size of the memory block should be given in number of elements and element size, and it initializes the allocated memory block to zero. The realloc () resizes the already allocated memory block while keeping its original content (if a bigger is allocated). The free () deallocates the reserved memory block.

In case of objects, the new and delete operators should be used to access all relating operations.

7.2.1. One-dimensional dynamic arrays

It is one-dimensional dynamic arrays that are the most frequently used in programming.

type *pointer;

pointer = newtype [number_of_elements];

or

pointer = new (nothrow) type [number_of_elements];

The difference between the two methods can only be perceived if allocation has not been successful. In the first case, the compiler throws an exception ( bad_alloc ) if there is not enough available contiguous memory, and in the second case, a pointer with the value of 0 is returned.

In case of arrays, it is extremely important not to forget about deleting allocated memory:

delete [] pointer;

In the following example, the user is asked to enter the array size. Then a memory block is allocated for the array and finally it is filled up with random numbers.

#include <iostream>

// Deleting (freeing) allocated memory space delete[] data;

return 0;

}

7.2.2. Two-dimensional dynamic arrays

We have already mentioned in the previous subchapters that two-dimensional arrays are actually vectors of one-dimensional vectors (rows). As a consequence, a two-one-dimensional array of any size cannot be created at runtime since compiler has to know the type (the size) of the elements (the row vectors). However, the number of the elements (the rows) can be provided later. The new in the following example returns a pointer to the created two-dimensional array.

const int rowlength = 4;

int number_of_rows;

cout<<"Number of rows: "; cin >> number_of_rows;

int (*mp)[rowlength] = new int [number_of_rows][rowlength];

The solution becomes much more understandable if the keyword typedef is used:

const int rowlength = 4;

int number_of_rows;

cout<<"Number of rows: "; cin >> number_of_rows;

typedef int rowtype[rowlength];

rowtype *mp = new rowtype [number_of_rows];

For both solutions, setting all elements to zero can be carried out by the following loops:

for (int i=0; i<number_of_rows; i++) for (int j=0; j<rowlength; j++) mp[i][j]=0;

where mp[i] refers to the ith row, and mp[i][j] refers to the jth element of the ith row. If the former statements are placed within the main () function, the mp pointer is placed in stack while the whole two-dimensional array in the heap. This solution has two drawbacks: arrays need memory space on the first hand, and it is annoying to provide the length of rows compile-time on the other hand.

Let's see how to avoid the constraints of memory and those of fixed row length. The next two solutions are based on the idea that arrays can be made from pointers.

I.19. ábra - Dynamically allocated row vectors

In the first case, the pointer array fixing the number of rows is created in the stack and it is only row vectors that are created dynamically (I.19. ábra - Dynamically allocated row vectors).

• The pointer vector of 3 elements that selects the rows: int* ps[3]= {0};

• The dynamic creation of rows, if rows have 4 elements:

for (int i=0; i<3; i++) ps[i] = new int[4];

• Elements can be accessed by the operators * and []:

*(ps[1] + 2) = 123;

cout << ps[1][2];

• Finally, we should not forget about freeing the dynamically allocated memory blocks.

for (int i=0; i<3; i++) delete[] ps[i];

If the number of rows and columns are to be set runtime, the pointer array of the previous example should be defined dynamically. In that case, stack only contains one pointer of type int * (I.20. ábra - Dynamically allocated pointer vector and row vectors).

Memory allocation, access and deallocation can easily be traced in the following example code:

#include <iostream>

using namespace std;

int main() {

int number_of_rows, rowlength;

cout<<"Number of rows: "; cin >> number_of_rows;

cout<<"The length of rows: "; cin >> rowlength;

// Memory allocation int* *pps;

// Defining the pointer vector pps = new int* [number_of_rows];

// Allocating rows

for (int i=0; i<number_of_rows; i++) pps[i] = new int [rowlength];

// Accessing the array

for (int i=0; i<number_of_rows; i++) for (int j=0; j<rowlength; j++) pps[i][j]=0;

// Deleting (freeing) allocated memory space for (int i=0; i<number_of_rows; i++)

delete pps[i];

delete pps;

}

I.20. ábra - Dynamically allocated pointer vector and row vectors