C++ Dynamic Allocation of Arrays with Example
A dynamic array is quite similar to a regular array, but its size is modifiable during program runtime. DynamArray elements occupy a contiguous block of memory.
Once an array has been created, its size cannot be changed. However, a dynamic array is different. A dynamic array can expand its size even after it has been filled.
During the creation of an array, it is allocated a predetermined amount of memory. This is not the case with a dynamic array as it grows its memory size by a certain factor when there is a need.
Factors impacting performance of Dynamic Arrays
The array’s initial size and its growth factor determine its performance. Note the following points:
- If an array has a small size and a small growth factor, it will keep on reallocating memory more often. This will reduce the performance of the array.
- If an array has a large size and a large growth factor, it will have a huge chunk of unused memory. Due to this, resize operations may take longer. This will reduce the performance of the array.
The new Keyword
In C++, we can create a dynamic array using the new keyword. The number of items to be allocated is specified within a pair of square brackets. The type name should precede this. The requested number of items will be allocated.
Syntax
The new keyword takes the following syntax:
pointer_variable = new data_type;
The pointer_variable is the name of the pointer variable.
The data_type must be a valid C++ data type.
The keyword then returns a pointer to the first item. After creating the dynamic array, we can delete it using the delete keyword.
Example 1:
#include using namespace std; int main() < int x, n; cout >n; int *arr = new int(n); cout > arr[x]; > cout return 0; >
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file into our program to use its functions.
- Include the std namespace in our program in order to use its classes without calling it.
- Call the main() function. The program logic should be added within the body of the function.
- Declare two integer variables x and n.
- Print some text on the console prompting the user to enter the value of variable n.
- Read user input from the keyboard and assigning it to variable n.
- Declare an array to hold a total of n integers and assigning it to pointer variable *arr.
- Print a message prompting the user to enter n number of items.
- Use a for loop to create a loop variable x to iterate over the items entered by the user.
- Read the elements entered by the user and storing them in the array arr.
- End of the body of the for loop.
- Print some text on the console.
- Use a for loop to create a loop variable x to iterate over the items of the array.
- Print out the values contained in the array named arr on the console.
- End of the body of the for loop.
- The program must return value upon successful completion.
- End of the body of the main() function.
NOTE: In the above example, the user is allowed to specify any size for the array during run time. This means the array’s size is determined during runtime.
Initializing dynamically allocated arrays
It’s easy to initialize a dynamic array to 0.
In the above syntax, the length denotes the number of elements to be added to the array. Since we need to initialize the array to 0, this should be left empty.
We can initialize a dynamic array using an initializer list. Let’s create an example that demonstrates this.
Example 2:
#include using namespace std; int main(void) < int x; int *array< new int[5]< 10, 7, 15, 3, 11 >>; cout return 0; >
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file into our program to use its functions.
- Include the std namespace in our program to use its classes without calling it.
- Call the main() function. The program logic should be added within the body of the function.
- Declare an integer variable named x.
- Declare a dynamic array named array using an initializer list. The array will hold 5 integer elements. Note that we’ve not used the “=” operator between the array length and the initializer list.
- Print some text on the console. The endl is a C++ keyword that means end line. It moves the cursor to the next sentence.
- Use a for loop to iterate over the array elements.
- Print the contents of the array named array on the console.
- End of the body of the for loop.
- The program must return value upon successful completion.
- End of the body of the main() function.
Resizing Arrays
The length of a dynamic array is set during the allocation time.
However, C++ doesn’t have a built-in mechanism of resizing an array once it has been allocated.
You can, however, overcome this challenge by allocating a new array dynamically, copying over the elements, then erasing the old array.
Note: that this technique is prone to errors, hence, try to avoid it.
Dynamically Deleting Arrays
A dynamic array should be deleted from the computer memory once its purpose is fulfilled. The delete statement can help you accomplish this. The released memory space can then be used to hold another set of data. However, even if you do not delete the dynamic array from the computer memory, it will be deleted automatically once the program terminates.
To delete a dynamic array from the computer memory, you should use delete[], instead of delete. The [] instructs the CPU to delete multiple variables rather than one variable. The use of delete instead of delete[] when dealing with a dynamic array may result in problems. Examples of such problems include memory leaks, data corruption, crashes, etc.
Example 3:
#include using namespace std; int main() < int x, n; cout >n; int *arr = new int(n); cout > arr[x]; > cout cout
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file in our program in order to use its functions.
- Include the std namespace in our program in order to use its classes without calling it.
- Call the main() function. The program logic should be added within the body of the function.
- Declare two variables x and n of the integer data type.
- Print some text on the console. The text will ask the user to state the number of numbers they will enter.
- Read user input from the keyboard. The input value will be assigned to variable n.
- Declare a pointer variable *arr. The array arr will reserve some memory to store a total of n integers.
- Print a message on the console prompting the user to enter n numbers.
- Create a for loop and the loop variable x to iterate over the numbers entered by the user.
- Read the numbers entered by the user and storing them in the array arr.
- End of the body of the for loop.
- Print some text on the console.
- Use a for loop and the loop variable x to iterate over the contents of array arr.
- Print out the values of the array arr on the console.
- End of the body of the for loop.
- Print an empty line on the console.
- Free up the memory of the array arr.
- The program will return value when it completes successfully.
- End of the body of the main() function.
Summary
- Regular arrays have a fixed size. You cannot modify their size once declared.
- With these types of arrays, the memory size is determined during compile time.
- Dynamic arrays are different. Their sizes can be changed during runtime.
- In dynamic arrays, the size is determined during runtime.
- Dynamic arrays in C++ are declared using the new keyword.
- We use square brackets to specify the number of items to be stored in the dynamic array.
- Once done with the array, we can free up the memory using the delete operator.
- Use the delete operator with [] to free the memory of all array elements.
- A delete without [] frees the memory of only a single element.
- There is no built-in mechanism to resize C++ arrays.
- To initialize an array using a list initializer, we don’t use the “=” operator.
Dynamic array in cpp
Кроме отдельных динамических объектов в языке C++ мы можем использовать динамические массивы. Для выделения памяти под динамический массив также используется оператор new , после которого в квадратных скобках указывается, сколько массив будет содержать объектов:
int *numbers ; // динамический массив из 4 чисел // или так // int *numbers = new int[4];
Причем в этом случае оператор new также возвращает указатель на объект типа int — первый элемент в созданном массиве.
В данном случае определяется массив из четырех элементов типа int, но каждый из них имеет неопределенное значение. Однако мы также можем инициализировать массив значениями:
int *numbers1 >; // массив состоит из чисел 0, 0, 0, 0 int *numbers2 >; // массив состоит из чисел 1, 2, 3, 4 int *numbers3 >; // массив состоит из чисел 1, 2, 0, 0 // аналогичные определения массивов // int *numbers1 = new int[4]<>; // массив состоит из чисел 0, 0, 0, 0 // int *numbers1 = new int[4](); // массив состоит из чисел 0, 0, 0, 0 // int *numbers2 = new int[4]< 1, 2, 3, 4 >; // массив состоит из чисел 1, 2, 3, 4 // int *numbers3 = new int[4]< 1, 2 >; // массив состоит из чисел 1, 2, 0, 0
При инициализации массива конкретными значениями следует учитывать, что если значений в фигурных скобках больше чем длина массива, то оператор new потерпит неудачу и не сможет создать массив. Если переданных значений, наоборот, меньше, то элементы, для которых не предоставлены значения, инициализируются значением по умолчанию.
Стоит отметить, что в стандарт С++20 добавлена возможность выведения размера массива, поэтому, если применяется стандарт С++20, то можно не указывать длину массива:
int *numbers >; // массив состоит из чисел 1, 2, 3, 4
После создания динамического массива мы сможем с ним работать по полученному указателю, получать и изменять его элементы:
int *numbers >; // получение элементов через синтаксис массивов std::coutПричем для доступа к элементам динамического массива можно использовать как синтаксис массивов ( numbers[0] ), так и операцию разыменования ( *numbers )
Соответственно для перебора такого массива можно использовать различные способы:
unsigned n< 5 >; // размер массива int* p < new int[n] < 1, 2, 3, 4, 5 >>; // используем индексы for (unsigned i<>; i < n; i++) < std::cout std::cout ; i < n; i++) < std::cout std::cout ; q != p + n; q++) < std::cout std::coutОбратите внимание, что для задания размера динамического массива мы можем применять обычную переменную, а не константу, как в случае со стандартными массивами.
Для удаления динамического массива и освобождения его памяти применяется специальная форма оператора delete :
delete [] указатель_на_динамический_массив;#include int main() < unsigned n< 5 >; // размер массива int* p < new int[n] < 1, 2, 3, 4, 5 >>; // используем индексы for (unsigned i<>; i < n; i++) < std::cout std::cout
Чтобы после освобождения памяти указатель не хранил старый адрес, также рекомендуется обнулить его:
delete [] p; p = nullptr; // обнуляем указательМногомерные массивы
Также мы можем создавать многомерные динамические массивы. Рассмотрим на примере двухмерных массивов. Что такое по сути двухмерный массив? Это набор массив массивов. Соответственно, чтобы создать динамический двухмерный массив, нам надо создать общий динамический массив указателей, а затем его элементы - вложенные динамические массивы. В общем случае это выглядит так:
#include int main() < unsigned rows = 3; // количество строк unsigned columns = 2; // количество столбцов int** numbers>; // выделяем память под двухмерный массив // выделяем память для вложенных массивов for (unsigned i<>; i < rows; i++) < numbers[i] = new int[columns]<>; > // удаление массивов for (unsigned i<>; i < rows; i++) < delete[] numbers[i]; >delete[] numbers; >Вначале выделяем память для массива указателей (условно таблицы):
Затем в цикле выделяем память для каждого отдельного массива (условно строки таблицы):
Освобождение памяти идет в обратном порядке - сначала освобождаем память для каждого отдельного вложенного массива, а затем для всего массива указателей.
Пример с вводом и выводом данных двухмерного динамического массива:
#include int main() < unsigned rows = 3; // количество строк unsigned columns = 2; // количество столбцов int** numbers>; // выделяем память под двухмерный массив for (unsigned i<>; i < rows; i++) < numbers[i] = new int[columns]<>; > // вводим данные для таблицы rows x columns for (unsigned i<>; i < rows; i++) < std::cout ; j < columns; j++) < std::cout > numbers[i][j]; > > // вывод данных for (unsigned i<>; i < rows; i++) < // выводим данные столбцов i-й строки for (unsigned j<>; j < columns; j++) < std::cout std::cout for (unsigned i<>; i < rows; i++) < delete[] numbers[i]; >delete[] numbers; >Enter data for 1 row 1 column: 2 2 column: 3 Enter data for 2 row 1 column: 4 2 column: 5 Enter data for 3 row 1 column: 6 2 column: 7 2 3 4 5 6 7Указатель на массив
От типа int** , который представляет указатель на указатель (pointer-to-pointer) следует отличать ситуацию "указатель на массив" (pointer to array). Например:
#include int main() < unsigned n; // количество строк int (*a)[2] = new int[n][2]; int k<>; // устанавливаем значения for (unsigned i<>; i < n; i++) < // устанавливаем данные для столбцов i-й строки for (unsigned j<>; j < 2; j++) < a[i][j] = ++k; >> // вывод данных for (unsigned i<>; i < n; i++) < // выводим данные столбцов i-й строки for (unsigned j<>; j < 2; j++) < std::cout std::cout // удаляем данные delete[] a; a = nullptr; >Здесь запись int (*a)[2] представляет указатель на массив из двух элементов типа int. Фактически мы можем работать с этим объектом как с двухмерным массивом (таблицей), только количество столбцов в данном случае фиксировано - 2. И память для такого массива выделяется один раз:
То есть в данном случае мы имеем дело с таблице из n строк и 2 столцов. Используя два индекса (для строки и столца), можно обращаться к определенному элементу, установить или получить его значение. Консольный вывод данной программы: