Форум программистов, компьютерный форум, киберфорум
Наши страницы
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.75/4: Рейтинг темы: голосов - 4, средняя оценка - 4.75
Alexey104
3 / 3 / 0
Регистрация: 26.10.2014
Сообщений: 101
1

Многомерные массивы

08.10.2015, 17:12. Просмотров 770. Ответов 8
Метки нет (Все метки)

Здравствуйте, уважаемые форумчане!
Давненько я не задавал здесь своих глупых вопросов по плюсам. Надо бы исправить ситуацию:
В общем, я всегда думал, что хорошо понимаю указатели, но, встретив во многих кодах двойные и даже тройные указатели, решил поэкспериментировать и понял, что не так уж хорошо и понимаю тему. Вот, например:
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
#include <iostream>
 
using namespace std;
 
int main()
{
  int *arr[3];
  arr[0] = new int(3);
  arr[0][0] = 100;
  arr[0][1] = 200;
  arr[0][2] = 300;
  int ***ptr[3];
  //ptr[0] = &arr; 
  ptr[0] = new int **[3];
  ptr[0][0] = arr;
  cout << (*ptr[0][0])[0] << "\n";
  cout << (*ptr[0][0])[1] << "\n";
  cout << (*ptr[0][0])[2] << "\n";
  /*delete [] *ptr[0][0];
  *ptr[0][0] = NULL;
  delete [] ptr[0][0];
  ptr[0][0] = NULL;
  delete [] ptr[0];
  ptr[0] = NULL;
  delete [] ptr;
  ptr = NULL;*/
  return 0;
}
Данную конструкцию я представляю себе следующим образом:
Многомерные массивы


1) Не понимаю, почему не компилируется строка 13. В этой строке мы сначала обращаемся к содержимому нулевой ячейки голубого(на схеме) массива - это тройной указатель, который должен содержать адрес указателя на указатель на тип int. Я и передаю ему адрес такого указателя, которым является arr. По-моему, всё логично, но GCC со мной не согласен:
error: cannot convert ‘int* (*)[3]’ to ‘int***’ in assignment
ptr[0] = &arr;

При этом такой код компилятором хавается:
C++
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
 
using namespace std;
 
int main()
{
  int **arr;
  arr = new int*(new int(5));
  int ***ptr = &arr;
  return 0;
}
2) Ума не приложу, как теперь освободить память, выделенную под весь этот мусор. Пытаюсь это сделать в строках 19-26:

В строке 19 освобождаем память, выделенную под красный массив - компилируется.

В строке 21 освобождаем память, выделенную под чёрный массив - компилируется, но падает при выполнении:
Многомерные массивы


В строке 23 освобождаем память, выделенную под зелёный массив - компилируется.

В строке 25 освобождаем память от голубого массива - GCC пишет:
warning: deleting array ‘ptr’
delete [] ptr;
^
Я понимаю, что пытаюсь удалить массив, а почему бы нет? Ведь указатели, содержащиеся в этом массиве хранятся в каких-то ячейках памяти, которые я и хочу освободить от этих самых указателей. Почему нельзя?

В общем, вопроса два:
1) Почему не компилируется строка 13?
2) Как правильно освободить память от всех этих массивов?
0
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
08.10.2015, 17:12
Ответы с готовыми решениями:

Многомерные массивы, как перебирать внутренние массивы
Здравствуйте. Такой учебный код и плохо понимаю как перебираются внутренние массивы, может кто...

Многомерные динамические массивы
Написать программу, которая дает пользователю ввести 5 фамилий студентов, а затем сортирует их по...

Многомерные массивы (матрицы)
Не могу понять как написать код. Если не сложно напишите подробнее цикл. Вот начало. ...

Многомерные массивы. Матрица.
Всем привет. Прошу помочь с заданием по программированию, не до конца понимаю кое-что… Задание:...

Многомерные массивы и STL
Господа, не подскажете, как создавать многомерные массивы, например с помощью &lt;vector&gt; ? И,...

8
Kuzia domovenok
2542 / 2223 / 555
Регистрация: 25.03.2012
Сообщений: 8,030
Записей в блоге: 1
Завершенные тесты: 1
08.10.2015, 17:23 2
начнём с того, что это не массив, а одинокий инт, с инициализацией его значением 3. (*arr[0] == 3)
Цитата Сообщение от Alexey104 Посмотреть сообщение
C++
1
arr[0] = new int(3);
на этом и закончим.
0
Alexey104
3 / 3 / 0
Регистрация: 26.10.2014
Сообщений: 101
08.10.2015, 18:19  [ТС] 3
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
начнём с того, что это не массив, а одинокий инт, с инициализацией его значением 3
Да, конечно, имелось-в-виду:
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
#include <iostream>
 
using namespace std;
 
int main()
{
  int *arr[3];
  arr[0] = new int[3];
  arr[0][0] = 100;
  arr[0][1] = 200;
  arr[0][2] = 300;
  int ***ptr[3];
  //ptr[0] = &arr; 
  ptr[0] = new int **[3];
  ptr[0][0] = arr;
  cout << (*ptr[0][0])[0] << "\n";
  cout << (*ptr[0][0])[1] << "\n";
  cout << (*ptr[0][0])[2] << "\n";
  //delete [] *ptr[0][0];
  //*ptr[0][0] = NULL;
  //delete [] ptr[0][0];
  //ptr[0][0] = NULL;
  //delete [] ptr[0];
  //ptr[0] = NULL;
  //delete [] ptr;
  //ptr = NULL;
  return 0;
}
Но ошибки при компиляции всё те же, и вопросы, соответственно, тоже.
0
Alexey104
3 / 3 / 0
Регистрация: 26.10.2014
Сообщений: 101
08.10.2015, 22:06  [ТС] 4
Пожалуй, слишком много фигни я нагородил в этом посте. Попробую адекватно сформулировать вопросы. Итак, абстрагируемся от всего вышесказанного и рассмотри пару простеньких кодов:
C++
1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
  int **arr = new int*[3];
  arr[0] = new int[3];
  arr[1] = new int[3];
  arr[2] = new int[3];
  delete [] arr[0];
  delete [] arr[1];
  delete [] arr[2];
  delete [] arr;
  return 0;
}
Здесь картина следующая:
Многомерные массивы

В строках 3-6 объявляем указатель на массив(голубой) из трёх указателей на массивы(красные) типа int, в строках 7-9 освобождаем область памяти, выделенную под красные массивы, а в строке 10 освобождаем память, выделенную под голубой массив. Указатель arr будет удалён при выходе из области видимости. Всё понятно.
А как насчёт такого кода:
C++
1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
  int *arr[3];
  arr[0] = new int[3];
  arr[1] = new int[3];
  arr[2] = new int[3];
  delete [] arr[0];
  delete [] arr[1];
  delete [] arr[2];
  //delete [] arr;
  return 0;
}
???
Здесь конструкция аналогичная. В строках 7-9 освобождаем память, выделенную под красные массивы, но голубой-то массив указателей по-прежнему существует, надо бы нам освободить память, выделенную под него. Это я и пытаюсь сделать в строке 10, на что GCC меня успешно посылает:
warning: deleting array ‘ptr’
delete [] ptr;
^

Почему во втором случае строка 10 не компилируется и как в этом случае освободить память от голубого массива указателей?
0
08.10.2015, 22:06
Alexey104
3 / 3 / 0
Регистрация: 26.10.2014
Сообщений: 101
08.10.2015, 23:38  [ТС] 5
Вот ведь я баран!
Дошло, наконец!
Цитата Сообщение от Alexey104 Посмотреть сообщение
int *arr[3];
Голубой массив-то в этом случае хранится не в куче, а в стеке - ясен пень, вызов оператора delete для него невозможен!
Пойду убьюсь об стену...

Добавлено через 59 минут
Не хотелось бы показаться совсем шизанутым, но остался последний вопрос:
C++
1
2
3
4
5
6
7
8
int main()
{
  //int *arr[5];
  int **arr;
  int ***ptr;
  ptr = &arr;
  return 0;
}
Если в этом коде раскомментировать строку 7 и вместо неё закомментировать строку 8, GCC выдаёт:
error: cannot convert ‘int* (*)[5]’ to ‘int***’ in assignment
ptr = &arr;
^
Почему? Ведь, как в строке 7, так и в строке 8, arr является указателем на другой указатель, который, в свою очередь, указывает на тип int. Правда, указатель arr в строке 7 указывает не просто на указатель, а на массив указателей(нулевую ячейку этого массива), но ведь для компилятора разницы быть не должно. Так почему не компилируется?
0
DrOffset
9868 / 5334 / 1306
Регистрация: 30.01.2014
Сообщений: 8,587
08.10.2015, 23:58 6
Лучший ответ Сообщение было отмечено Alexey104 как решение

Решение

Цитата Сообщение от Alexey104 Посмотреть сообщение
error: cannot convert ‘int* (*)[3]’ to ‘int***’ in assignment
Здесь пока что нужно не торопиться и внимательно поизучать тему массивов.
Главное, что нужно уяснить: массив - это не указатель!
Массив только приводится к указателю неявно, но у массива свой тип.
Вот тут:
C++
1
ptr[0] = &arr;
ты использовал операцию взятия адреса на переменной имеющий тип "массив указателей из трех элементов". результат этого - указатель на массив указателей из трех элементов (та "странная" запись int*(*)[3] - это оно и есть).
К тому же, у тебя все равно второй массив имеет больше косвенностей, чем нужно. Можно было бы присвоить адрес первого элемента (который по значению совпадает с адресом массива) вот таким кодом:
C++
1
2
ptr[0] = &arr[0]; 
// или таким: ptr[0] = arr;
но, т.к. массив ptr объявлен так
C++
1
int ***ptr[3];
а не так
C++
1
int **ptr[3];
то мы опять получим ошибку, но уже другую: assigning to 'int ***' from incompatible type 'int **'. Посмотри внимательно на первый массив arr и на второй ptr - и ты поймешь почему так.

Разбирайся.

Добавлено через 11 минут
Цитата Сообщение от Alexey104 Посмотреть сообщение
но ведь для компилятора разницы быть не должно
Должно. И разница есть. Типизация у С++ статическая. Типы содержат метаинформацию (например размер). Так вот, указатель на массив содержит информацию о размерности массива (компилятор тебе об этом пишет явно). Эту информацию компилятор может использовать, например, для предоставления адресной арифметики на таком указателе. +1 - сместит значения адреса на размер целого массива (sizeof(int*) * 3). Поэтому нет сомнений, что указатель на массив из трех указателей и указатель на массив из четырех указателей будут вести себя по-разному при использовании адресной арифметики, а неявное преобразование такого указателя в обычный - приведет к потере информации о размере. Следовательно нет веских причин разрешать такое преобразование, как нет причин разрешать неявное преобразование int * -> double *.
Приведение же имени массива в указатель на первый элемент - это особый, исключительный случай, который полностью перешел из С. Поэтому тут существует некая поблажка. Однако все остальные случаи попадают под правила более строгой, чем в С типизации, которая не разрешает просто так приводить типы. Например в С разрешается приводить несовместимые по типам указатели неявно почти в любых случаях, но в С++ - это запрещено. Поэтому за исключением некоторых принципиальных вопросов совместимости с С, С++ всегда будет контролировать типизацию, защищая тем самым от потенциальных ошибок.
1
Alexey104
3 / 3 / 0
Регистрация: 26.10.2014
Сообщений: 101
09.10.2015, 00:14  [ТС] 7
Цитата Сообщение от DrOffset Посмотреть сообщение
Должно. И разница есть. Типизация у С++ статическая. Типы содержат метаинформацию (например размер). Так вот, указатель на массив содержит информацию о размерности массива (компилятор тебе об этом пишет явно). Эту информацию компилятор может использовать, например, для предоставления адресной арифметики на таком указателе. +1 - сместит значения адреса на размер целого массива (sizeof(int*) * 3). Поэтому нет сомнений, что указатель на массив из трех указателей и указатель на массив из четырех указателей будут вести себя по-разному при использовании адресной арифметики, а неявное преобразование такого указателя в обычный - приведет к потере информации о размере. Следовательно нет веских причин разрешать такое преобразование, как нет причин разрешать неявное преобразование int * -> double *.
Приведение же имени массива в указатель на первый элемент - это особый, исключительный случай, который полностью перешел из С. Поэтому тут существует некая поблажка. Однако все остальные случаи попадают под правила более строгой, чем в С типизации, которая не разрешает просто так приводить типы. Например в С разрешается приводить несовместимые по типам указатели неявно почти в любых случаях, но в С++ - это запрещено. Поэтому за исключением некоторых принципиальных вопросов совместимости с С, С++ всегда будет контролировать типизацию, защищая тем самым от потенциальных ошибок.
DrOffset, Большое спасибо! Этого я не знал.

Добавлено через 9 минут
Цитата Сообщение от DrOffset Посмотреть сообщение
та "странная" запись int*(*)[3] - это оно и есть
А не подскажете, по какой причине вторая звёздочка в этом сообщении заключена в круглые скобки? По-идее, круглые скобки должны задавать приоритет выполнения этой звёздочки над, к примеру, оператором '[]'. В чём смысл скобок именно в этом случае?
0
DrOffset
9868 / 5334 / 1306
Регистрация: 30.01.2014
Сообщений: 8,587
09.10.2015, 00:36 8
Цитата Сообщение от Alexey104 Посмотреть сообщение
Большое спасибо! Этого я не знал.
Тут простая вещь зарыта на самом деле. Для многих это не понятно, потому что они сразу начинают ассоциировать то, что пишешь в коде, с ассемблером, а адресами, регистрами и т.п. Естественно у человека возникает недоумение, как это так, адрес-то одинаковый, чего же он не кастится!
Рассматривать так код тоже правильно и полезно, но этого мало. С++ высокоуровневый язык, у которого есть своя система типов. Система типов определяет правила игры при компиляции. И код компилятор строит опираясь на нее. Поэтому при анализе программы, прежде чем спуститься на низкий уровень, также полезно понимать что происходит во время компиляции, как типы в программе взаимодействуют в рамках правил языка.

Добавлено через 8 минут
Цитата Сообщение от Alexey104 Посмотреть сообщение
А не подскажете, по какой причине вторая звёздочка в этом сообщении заключена в круглые скобки? По-идее, круглые скобки должны задавать приоритет выполнения этой звёздочки над, к примеру, оператором '[]'.
Именно приоритет они и задают. Только в данном случае речь пойдет о приоритете разбора.
См. например, вот эту статью: http://habrahabr.ru/post/100104/
Там как раз описан тот порядок, который используется при анализе таких выражений.
(*) - указатель, переходим направо
[3] - на массив из трех, переходим налево
int * - указателей на int.
Скобки указывают приоритет. Иначе вторая звездочка начнет относиться к левой части и мы получим совсем другое выражение:
[3] - массив их трех, переходим налево
int ** - указателей на указатель на int.

Добавлено через 6 минут
Вот еще полезная ссылка: https://msdn.microsoft.com/ru-ru/library/1x82y1z4.aspx
Плюс можешь поискать мои ответы на форуме на аналогичные темы.
1
Alexey104
3 / 3 / 0
Регистрация: 26.10.2014
Сообщений: 101
09.10.2015, 00:40  [ТС] 9
Спасибо, буду изучать...
0
09.10.2015, 00:40
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.10.2015, 00:40

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

Задача на Многомерные Массивы
Фирма имеет 5 магазинов. Информация о доходе каждого магазина за каждый месяц хранится в двухмерном...

Многомерные массивы и строки.
Извиняюсь, что просто с нуля, но очень надо. Будьте добры) Программирование на языках С 1)Дана...


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

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru