C++: Возврат динамического массива из функции через ее параметр-указатель

Aug 14, 2024 00:16

У студента задача реализовать в коде алгоритм примерно такого вида (задание было несколько сложнее, я упрощаю):

// создать указатель на одномерный массив целых чисел

// вызвать функцию, которая создаст динамический массив, заполнит его данными
// и вернет его адрес в вызывающую функцию; полученный адрес нужно сохранить
// в созданный выше указатель на массив

// вывести элементы созданного массива в консоль

// освободить память, занятую массивом, когда он станет не нужен

Упомянутую функцию тоже нужно предварительно написать.

Я объяснил, что это можно написать разными способами, и предложил попробовать два варианта: возврат динамического массива из функции обычным способом (с помощью команды return) или через параметр-указатель.

1. Возврат динамического массива из функции с помощью return

Этим способом задачу решили довольно быстро, особых проблем не возникло. В интернетах по этой теме полно примеров (например). Получилось что-то вроде следующего.

#include

int* createDynArray(int &size)
{
std::cout << "Введите размер массива: ";
std::cin >> size; // получить размер массива
int *arr = new int[size]; // выделить память под массив
for (int i{}; i < size; i++) arr[i] = i; // заполнить массив данными
return arr; // возвратить массив
}

int main()
{
// создать указатель на одномерный массив целых чисел
int *arr{ nullptr }; // указатель на первый элемент массива
int size{}; // размер массива

// вызвать функцию, которая создаст динамический массив, заполнит его данными
// и вернет его адрес в вызывающую функцию; полученный адрес нужно сохранить
// в созданный выше указатель на массив
arr = createDynArray(size);

// вывести элементы созданного массива в консоль
std::cout << "Полученный массив: ";
for (int i{}; i < size; i++)
std::cout << arr[i] << ' ';
std::cout << '\n';

// освободить память, занятую массивом, когда он станет не нужен
delete[] arr;

return 0;
}

Переменную size передаем в функцию по ссылке (это отмечено знаком амперсанда & в заголовке функции). Мы передаем в функцию пустую переменную, а функция в нее поместит размер созданного динамического массива, чтобы можно было найти окончание массива, когда мы будем перебирать его элементы для различных операций с массивом в программе. В вышеприведенной программе, например, мы используем заполненную функцией переменную size при выводе элементов массива в консоль.

Пример работы вышеприведенной программы в консоли:

Введите размер массива: 9
Полученный массив: 0 1 2 3 4 5 6 7 8

2. Возврат динамического массива из функции через параметр-указатель

Во втором случае что-то надолго задумались, а я не помнил точно, в чем там соль. Отложил себе на дом. На занятии написали следующее (привожу только фрагменты кода, отличающиеся от программы из пункта 1: меняем только функцию и ее вызов).

Ошибочное решение

void createDynArray(int *arr, int &size)
{
std::cout << "Введите размер массива: ";
std::cin >> size; // получить размер массива
arr = new int[size]; // выделить память под массив
for (int i{}; i < size; i++) arr[i] = i; // заполнить массив данными
}

// вызвать функцию, которая создаст динамический массив, заполнит его данными
// и вернет его адрес в вызывающую функцию; полученный адрес нужно сохранить
// в созданный выше указатель на массив
createDynArray(arr, size);

Программа с этим кодом аварийно заканчивает работу в тот момент, когда начинает выводить элементы созданного динамического массива в консоль. Позже, на свежую голову, я разобрался, в чем тут проблема.

Обычно студентам рассказывают, что есть три способа передать аргумент в функцию: по значению, по ссылке и по указателю. Способ «по значению» используется только для передачи данных внутрь функции, а способы «по ссылке» и «по указателю» используются и для передачи данных внутрь функции, и для возврата данных из функции.

В данном случае кажется, что мы используем способ передачи массива в функцию «по указателю» (поскольку параметр имеет указательный тип), а на самом деле тут - способ передачи «по значению» (в результате функция ничего не возвращает назад, программа пытается вывести массив по указателю arr, который мы проинициализировали значением nullptr; то есть выводить нечего, а программа пытается вывести и аварийно завершает работу).

Почему это так? В данном случае заковыка в том, что мы хотим передать «по указателю» массив, а имя массива само по себе является указателем на первый элемент этого массива. Чтобы здесь использовать способ передачи массива «по указателю», нужно использовать указатель на указатель.

Правильное решение

void createDynArray(int **arr, int &size)
{
std::cout << "Введите размер массива: ";
std::cin >> size; // получить размер массива
*arr = new int[size]; // выделить память под массив
for (int i{}; i < size; i++) (*arr)[i] = i; // заполнить массив данными
}

// вызвать функцию, которая создаст динамический массив, заполнит его данными
// и вернет его адрес в вызывающую функцию; полученный адрес нужно сохранить
// в созданный выше указатель на массив
createDynArray(&arr, size);

Обратите внимание, что при этом способе работы с массивом к указателю arr внутри функции приходится применять операцию разыменования, а при вызове функции к указателю arr приходится применять операцию взятия адреса.

Результат работы полученной программы в консоли будет такой же, как и в пункте 1 (при возврате динамического массива с помощью команды return).

Образование, Программирование, Школа

Previous post Next post
Up