Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.63/40: Рейтинг темы: голосов - 40, средняя оценка - 4.63
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415

Выделение памяти для динамического двумерного массива внутри функции

11.08.2017, 00:09. Показов 7824. Ответов 25
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Имеется вот такой код :
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
#include <iostream>
 
void allocateMemory(int ***array, int size);
void freeMemory(int **array, int size);
int main()
{
    int **matrix{ nullptr };
    constexpr int size{ 10 };
    allocateMemory(&matrix, size);
    freeMemory(matrix, size);
}
 
void allocateMemory(int ***array, int size)
{
    *array = new int *[size];
    for (int index{ 0 }; index < size; index++)
    {
        (*array)[index] = new int[size];
    }
}
 
void freeMemory(int **array, int size)
{
    for (int index{ 0 }; index < size; index++)
    {
        delete[] array[index];
    }
    delete[] array;
}
Он работает, память выделяет, если верить VS и rexster(GCC), даже чистит её.
Но я вот усиленно не могу понять, почему не работает такой вариант :
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void allocateMemory(int **array, int size)
{
    array = new int *[size];
    for (int index{ 0 }; index < size; index++)
    {
        array[index] = new int[size];
    }
}
int main()
{
    int **matrix{ nullptr };
    constexpr int size{ 10 };
    allocateMemory(matrix, size);
    freeMemory(matrix, size);
}
После возвращения из allocateMemory в таком случае matrix имеет значение nullptr, а значит, указатель на указатель передается по значению, но ведь его значение - и есть адрес указателя, указывающего на int.
Ведь имя массива - и есть адрес его первого элемента и идет передача значения, которое является адресом, то есть того, что нам нужно. По крайне мере, это работает на одномерных массивах. А вот здесь - ступор.
Причем работает и такой вариант функции освобождения памяти :
C++
1
2
3
4
5
6
7
8
void freeMemory(int ***array, int size)
{
    for (int index{ 0 }; index < size; index++)
    {
        delete[] (*array)[index];
    }
    delete[] *array;
}
Честно сказать, просто уже запутался с этими указателями на указатели, которые передаются не так, как хочется.
Около полугода назад встречался с похожим, но тогда проблему исправил методом тыка и как-то забылось. Сейчас совершенно случайно всплыло снова, причем со 2го раза также все удачно написалось, но суть проблемы я понимаю не очень. А это ведь еще даже не С++, а С, то есть совсем уже базовый уровень.
Разъясните, пожалуйста, как можно подробнее, в чем же здесь кроется секрет.
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
11.08.2017, 00:09
Ответы с готовыми решениями:

Выделение памяти для двухмерного массива внутри функции
Здравствуйте. Предположим, есть код: #include &lt;iostream&gt; #include &lt;iomanip&gt; #include &lt;cstdlib&gt; #include &lt;time.h&gt; ...

Как правильно записать в виде функции выделение памяти для двумерного массива и ее освобождение
Здравствуйте! Подскажите, как правильно записать в виде функции выделение памяти для двумерного массива и ее освобождение. ...

Выделение памяти для двумерного массива
Обьясните что означает double** A = new double*, A = new double;,cout.precision(2);, delete A;?????? #include &lt;iostream&gt; ...

25
nd2
3438 / 2817 / 1249
Регистрация: 29.01.2016
Сообщений: 9,427
11.08.2017, 00:26
Лучший ответ Сообщение было отмечено Notoriously как решение

Решение

Цитата Сообщение от Notoriously Посмотреть сообщение
Но я вот усиленно не могу понять, почему не работает такой вариант :
Потому, что передача по значению, копию передаёшь, в функции изменяется копия, а не оригинал (который в main). Передавай по ссылке, тогда будет меняться оригинал:
C++
1
void allocateMemory(int **& array, int size)
Добавлено через 9 минут
Цитата Сообщение от Notoriously Посмотреть сообщение
После возвращения из allocateMemory в таком случае matrix имеет значение nullptr, а значит, указатель на указатель передается по значению, но ведь его значение - и есть адрес указателя, указывающего на int.
Ведь имя массива - и есть адрес его первого элемента и идет передача значения, которое является адресом, то есть того, что нам нужно. По крайне мере, это работает на одномерных массивах.
Тут вообще нет массивов - размер массива известен на этапе компиляции, тут одни указатели. Если взять просто указатель, а не указатель на указатель, то, при передаче по значению, будет то же самое:
C++
1
2
3
4
5
6
7
8
9
10
11
void foo(int* p)
{
    p = new int[10];
}
 
int main()
{  
    int* p = nullptr;
    foo(p);
    cout << p << endl; // по-прежнему nullptr
}
1
4 / 4 / 1
Регистрация: 27.07.2017
Сообщений: 54
11.08.2017, 00:38
выделение и инициализация

C++
1
2
3
4
5
6
7
double **d = new double*[number_of_string];
for(int i = 0; i < number_of_string; i++){
d[i] = new double[number_of_stolb];
for(int j = 0; j <number_of_stolb; j++){
if(j==3){d[i][j]=1;}else{
 d[i][j]=1; }}
}
0
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
11.08.2017, 00:45  [ТС]
Цитата Сообщение от nd2 Посмотреть сообщение
Потому, что передача по значению, копию передаёшь, в функции изменяется копия, а не оригинал
Но ведь копия получает значение параметра и по ней выделяет память. Или при завершении функции эта память освобождается, так как была выделена для локальной копии параметра?
У меня никак не укладывается, почему передаешь указатель, значением которого является чей-то адрес, этот адрес ложится в параметр и все равно не происходит по нему выделения памяти после отработки функции.
Ведь это же адрес, лежит он в оригинале или же в локальной копии.
И почему freeMemory отрабатывает и при условии передачи "по значению"?
Вариант с ссылкой я знаю, спасибо, насколько я понимаю, это тоже самое, что передать int ***, только не нужно внутри функции делать дополнительное разыменование.
0
11.08.2017, 00:45

Не по теме:

Цитата Сообщение от T_e_n_Jl_bl_u Посмотреть сообщение
выделение и инициализация
А форматирование кода?

0
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
11.08.2017, 00:48  [ТС]
И еще есть такой вариант -
C++
1
2
3
4
5
6
7
8
9
int** allocateMemory(int dimension)
{
    int **array = new int *[dimension];
    for(int counter{0}; counter < dimension; counter++)
    {
        array[counter] = new int [dimension];
    }
    return array;
}
Почему в данном случае после отработки функции память, выделенная функцией в куче, не освобождается и остается валидной? То есть данный вариант у меня тоже отрабатывает корректно.
Или же память, выделенная внутри функции в куче и должна оставаться валидной?
0
What a waste!
 Аватар для gray_fox
1610 / 1302 / 180
Регистрация: 21.04.2012
Сообщений: 2,733
11.08.2017, 00:53
Цитата Сообщение от Notoriously Посмотреть сообщение
У меня никак не укладывается, почему передаешь указатель, значением которого является чей-то адрес, этот адрес ложится в параметр и все равно не происходит по нему выделения памяти после отработки функции.
Передаётся указатель по значению, т.е. его значение копируется. Ф-я выделения памяти возвращает адрес начала блока выделенной памяти и этот адрес присваивается локальной для ф-ии копии аргумента. Оригинальный аргумент ф-ии при этом не изменяется.
1
nd2
3438 / 2817 / 1249
Регистрация: 29.01.2016
Сообщений: 9,427
11.08.2017, 01:00
Цитата Сообщение от Notoriously Посмотреть сообщение
Почему в данном случае после отработки функции память, выделенная функцией в куче, не освобождается
То, что выделяется через new, освобождается через delete.
Цитата Сообщение от Notoriously Посмотреть сообщение
Или же память, выделенная внутри функции в куче и должна оставаться валидной?
Должна, на то она и куча.
1
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
11.08.2017, 01:00  [ТС]
gray_fox,
Понял. А в случае удаления, в локальную копию идет адрес, по которому идет удаление и соотвественно так как чистим то мы по переданному адресу, пусть он и лежит в копии, то по возвращению память будет чиста и под аргументом?
0
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
11.08.2017, 01:01  [ТС]
Цитата Сообщение от nd2 Посмотреть сообщение
Должна
Усиленно казалось, что где-то в литературе я видел обратное, хотя код прям-таки кричал об этом, спасибо за пояснение.
0
What a waste!
 Аватар для gray_fox
1610 / 1302 / 180
Регистрация: 21.04.2012
Сообщений: 2,733
11.08.2017, 01:01
Цитата Сообщение от Notoriously Посмотреть сообщение
в случае удаления, в локальную копию идет адрес, по которому идет удаление и соотвественно так как чистим то мы по переданному адресу, пусть он и лежит в копии, то по возвращению память будет чиста и под аргументом?
Да.
1
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
11.08.2017, 01:07  [ТС]
gray_fox, nd2,
Спасибо, понял.
0
19495 / 10100 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
11.08.2017, 09:04
Цитата Сообщение от Notoriously Посмотреть сообщение
Ведь имя массива - и есть адрес его первого элемента* и идет передача значения, которое является адресом, то есть того, что нам нужно. По крайне мере, это работает на одномерных массивах*. А вот здесь - ступор.
Пара замечаний по выделенным моментам.
* Имя массива в рамках системы типов С++ - приводится к указателю на первый элемент, именно приводится, а не является. Это важно, чтобы не заблуждаться в дальнейшем.
* Описанная ситуация на самом деле работает одинаково для всех типов, разница только в твоем конкретном восприятии наблюдаемых явлений. Т.е., чтобы быстрее добраться до истины, надо задавать себе вопрос, "что я не так понимаю?", а не "почему в языке что-то работает по-разному?". Верные вопросы - залог быстрого и верного ответа.
1
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
11.08.2017, 10:15  [ТС]
DrOffset,
А чем тогда является имя массива в контексте его обычного использования?
Просто адресом первого элемента, а не указателем на него?
То есть оно является указателем на первый элемент лишь внутри функции при передаче туда этого массива?
0
19495 / 10100 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
11.08.2017, 11:05
Цитата Сообщение от Notoriously Посмотреть сообщение
А чем тогда является имя массива в контексте его обычного использования?
Имя массива - это идентификатор переменной типа "массив". Точно так же, как, например, в записи int a; - a - это идентификатор переменной типа int. Т.е. о каких-либо указателях в контексте имени тут говорить не приходится. В то же время, у типа "массив" есть ограничения, связанные с копированием по значению. Поэтому существует семантика преобразования объектов такого типа в указатель.

Подобные неявные преобразования мы можем определить сами для своих классов. Например можно сделать так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <typename T, size_t N>
class Array
{
    Array & operator=(Array const &) = delete;
    Array(Array const &) = delete;
public:
    Array() {}
    
    operator T *()
    {
        return m_array;
    }
private:
    T m_array[N];
};
 
void foo(int * ptr) {}
 
int main()
{
    Array<int, 10> a;
    foo(a);
}
В общих чертах этот пример повторяет семантику языковых массивов, касательно преобразования в указатель. Но, я думаю, тебе бы даже не пришло в голову для этого примера утверждать, что a - это указатель на первый элемент.
0
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
11.08.2017, 20:27  [ТС]
DrOffset,
Вроде бы понятно.
В данном контексте идентификатор - абстракция, связывающая область памяти с конкретным именем для более удобного к ней обращения?
0
19495 / 10100 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
11.08.2017, 23:15
Цитата Сообщение от Notoriously Посмотреть сообщение
В данном контексте идентификатор - абстракция, связывающая область памяти с конкретным именем для более удобного к ней обращения?
Ага.
Но я не на этом внимание хотел акцентировать, а на типе. Тип у массива - "массив", а не указатель. Тип "массив" обладает определенным набором свойств и ограничений, и преобразование объектов в указатель - лишь одно из них. Вполне можно привести пример, когда именно тип "массив" будет определять поведение и семантику программы (например при разрешении перегрузки, или како-либо использование статический информации о размере), а не результат преобразования в указатель. Поэтому приравнивать массив к указателю, значит заведомо отметать все остальные случаи использования типа "массив". Вот это важно.
1
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
12.08.2017, 01:56  [ТС]
DrOffset,
Да, я понял ошибочность рассуждений. К сожалению я не помню, чтобы даже в K & R делался акцент или хотя бы что-то говорилось о том, что это идет лишь преобразование типа. Ну или опять же, я истолоковал неверно.
Спасибо.
0
19495 / 10100 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
12.08.2017, 03:00
Цитата Сообщение от Notoriously Посмотреть сообщение
я не помню, чтобы даже в K & R делался акцент или хотя бы что-то говорилось о том, что это идет лишь преобразование типа.
В K&R нет такого акцента. Несмотря на то, что там все-таки тоже будет преобразование.
Цитата Сообщение от Стандарт C
an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
Но K&R описывает С, а не С++. И это учебник, а не стандарт. В учебниках могут быть допуски, если автор посчитал какую-либо информацию несущественной.

В целом, могу посоветовать не делать выводов о С++ на основе учебников по С. Это все-таки разные языки, и поэтому во многих вопросах будут разные акценты. Если хочется верных акцентов, лучше читать стандарт. В стандарте С++, в частности, пункты: Array-to-pointer conversion, Arrays.
1
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
12.08.2017, 10:47  [ТС]
DrOffset,
Хорошо, спасибо, прочитаю.
Действительно полезная информация, так как до этого я на нее не натыкался ни в одном из учебников. Правда, пока что большинство из них и правда было по C.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
12.08.2017, 10:47
Помогаю со студенческими работами здесь

Динамическое выделение памяти для двумерного массива
нужна помощь вот тело программы, не могу сделать динамическое выделение памяти для первого двумерного массива. второй массив получается...

Выделение динамической памяти для двумерного массива.
#include &lt;iostream&gt; int main() { setlocale( LC_ALL,&quot;Russian&quot; ); int N, M; std::cout &lt;&lt; &quot;Введите кол-во строк в массиве:...

Если выделение памяти для динамического массива задать void-функцией, можно ли будет оперировать с ним в main?
Если выделение памяти для дин.массива задать void-функцией, можно ли будет оперировать с ним в main или придётся отдельно выделять память в...

Вызов функции динамического выделение памяти
Здравствуйте! Нужна помощь! Написал функцию выделение памяти под двумерный динамический массив, которая возвращает масив размером...

Инициализация двумерного динамического массива внутри программы
Здравствуйте! Подскажите, пожалуйста, можно ли как-то инициализировать двумерный динамический массив значениями без ввода при запуске?...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL3_image
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
влияние грибов на сукцессию
anaschu 26.01.2026
Бифуркационные изменения массы гриба происходят тогда, когда мы уменьшаем массу компоста в 10 раз, а скорость прироста биомассы уменьшаем в три раза. Скорость прироста биомассы может уменьшаться за. . .
Воспроизведение звукового файла с помощью SDL3_mixer при касании экрана Android
8Observer8 26.01.2026
Содержание блога SDL3_mixer - это библиотека я для воспроизведения аудио. В отличие от инструкции по добавлению текста код по проигрыванию звука уже содержится в шаблоне примера. Нужно только. . .
Установка Android SDK, NDK, JDK, CMake и т.д.
8Observer8 25.01.2026
Содержание блога Перейдите по ссылке: https:/ / developer. android. com/ studio и в самом низу страницы кликните по архиву "commandlinetools-win-xxxxxx_latest. zip" Извлеките архив и вы увидите. . .
Вывод текста со шрифтом TTF на Android с помощью библиотеки SDL3_ttf
8Observer8 25.01.2026
Содержание блога Если у вас не установлены Android SDK, NDK, JDK, и т. д. то сделайте это по следующей инструкции: Установка Android SDK, NDK, JDK, CMake и т. д. Сборка примера Скачайте. . .
Использование SDL3-callbacks вместо функции main() на Android, Desktop и WebAssembly
8Observer8 24.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
моя боль
iceja 24.01.2026
Выложила интерполяцию кубическими сплайнами www. iceja. net REST сервисы временно не работают, только через Web. Написала за 56 рабочих часов этот сайт с нуля. При помощи perplexity. ai PRO , при. . .
Модель сукцессии микоризы
anaschu 24.01.2026
Решили писать научную статью с неким РОманом
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru