Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

вопрос по размещению элементов динамического массива в памяти

257
21 мая 2008 года
kosfiz
1.6K / / 18.09.2005
опишу ситуацию. всегда думал, что элементы массива располагаются в памяти последовательно друг за другом(и деление на одномерные и многомерные массивы вроде как условно - по крайней мере с моей точки зрения) - это подтверждалось при работе со статическими массивами, да и в литературе тоже говориться об этом. думал, что динамические массивы обладают тем же свойством, т.е. при инициализации выделяется память и элементы располагаются друг за другом, но... если мы возьмем, к примеру, двумерный динамический массив
[highlight=delphi]
var
darr:array of array of byte;
{....................................}
SetLength(darr, 3, 10);
[/highlight]
то казалось бы за элементом darr[0, 9] в памяти должен идти darr[1, 0], однако если посмотреть то нет, в памяти между ними как бы пусто, т.е. они не идут друг за другом.
отсюда вопрос: может я все-таки ошибаюсь? но если данный "пробел" и вправду есть то всвязи с чем была выбрана такая реализация, на ваш взгляд? или может быть это нужно для выполнения какой-либо цели или несет какую-то функцию, уж не знаю? поделитесь вашим мнением по данному вопросу, очень интересно.
3.2K
21 мая 2008 года
Гудвин
186 / / 22.12.2007
думаю в случае с динамическим созданием мы получаем массив указателей на массивы байтов, которые располагаются не последовательно, а в зависимости от свободных мест в памяти. хотя, чтоб сказать точно, думаю стоит поковыряться в процедуре setlength. :rolleyes:
257
21 мая 2008 года
kosfiz
1.6K / / 18.09.2005
Цитата: Гудвин
думаю в случае с динамическим созданием мы получаем массив указателей на массивы байтов, которые располагаются не последовательно, а в зависимости от свободных мест в памяти. хотя, чтоб сказать точно, думаю стоит поковыряться в процедуре setlength. :rolleyes:


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

261
21 мая 2008 года
ahilles
1.5K / / 03.11.2005
Цитата: Гудвин
думаю в случае с динамическим созданием мы получаем массив указателей на массивы байтов, которые располагаются не последовательно, а в зависимости от свободных мест в памяти. хотя, чтоб сказать точно, думаю стоит поковыряться в процедуре setlength.


я тут немного потестил и убедился что именно так и есть: массив указателей на массивы байтов. Располагаются они не посдедовательно, и размер кадого подмассива округляется до числа кратного 16. (у меня была матрица 5х5, первые три массива были рядом, ещё два отдельно)

Цитата: kosfiz
тогда интересно, что мешает выделить память сразу под все элементы?!


по-моему создатели рукводоствовались правилом: найти большой свободный участок сложнее, чем найти множество маленьких. Тем более если есть много маленньких свободных участков, то выделение одного большого, будет расточительно, тем более если у нас массив большой размерности.

257
21 мая 2008 года
kosfiz
1.6K / / 18.09.2005
Цитата: ahilles
по-моему создатели рукводоствовались правилом: найти большой свободный участок сложнее, чем найти множество маленьких.


немного странно или может я чего не понимаю. когда мы объявляем статический массив(такой же размерности), то элементы располагаются последовательно,т.е. выделяется "большой"(в моем случае в 30 байт, невероятно большой:)) участок - здесь они уже своим "правилом" не пользуются? по меньшей мере странно и как я уже упоминал вносит некоторые неудобства. оправдано ли это - вопрос спорный.

261
21 мая 2008 года
ahilles
1.5K / / 03.11.2005
суть в том для статических массивов место выделяется ещё во время компиляции, там заранее всё известно (компилятор заранее всё "равненько расположит" в памяти), а когда используются динамические массивы, то всё происходит на лету. тем более при таком подходе намного проще изменить размерность многомерных массивов, если до этого массив располагался "впритык". допустим был 8х16 "впритык" (все 8 подмассивов раполагаются рядом), а надо 9х16, нам надо будет просто выделить ещё один одномерный массив размером 16 и всё
303
21 мая 2008 года
makbeth
1.0K / / 25.11.2004
Ну, как правило, менеджер кучи не заботиться об удобстве разработчика. Он заботится о производительности ;) Насчет пропусков - я думаю задумано специально, как некий резерв в случае, например, увеличения массива (повторного вызова SetLength), чтобы лишний раз не делать релокацию блоков памяти. Вспомним хотябы такое замечательное свойство у классов списков - Capacity. Ну а насчет удобства "в некоторых случаях" (речь, я так понимаю, идет о работе с указателями), то динамические массивы для этого не предназначены. Проще использовать блоки а-ля GetMem/FreeMem.
16K
21 мая 2008 года
Alfá
59 / / 12.01.2007
Не, все, что вы писали, ребята неверно.
kosfiz, смотри, как работает выделение динамического массива. Ну, на то он и динамическим называется, что память под него распределяется в ходе выполнения программы. Для твоего примера с двумерным массивом выделение происходит дважды. Для общего случая – столько, какова размерность многомерного массива.
Конкретно для твоего примера, а общий случай будет почти аналогичным. Сперва выделяется массив указателей. Не для того, чтобы выставить тебе незнающим, тупым и т.д., а просто для логики размышления и для понимания другими читающими, напомню, что указатель – это переменная, которая в качестве своего значения хранит адрес другой ячейки памяти, где действительно находятся данные.
Так вот, выделяется массив указателей. Он последовательный. Фактически - это размерность по первому измерению твоего исходного массива. Ну а так как система 32-хразрядная, то выделится 3 двойных слова. К примеру, это такие слова:
 
Код:
10101010    20202020    30303030    40404040

Данные же заносятся во второй массив, но он, как таковой не выделяется. Другими словами: машина берет первый массив и по адресу, указанному в нем, заносит само значение. Второе значение из твоего исходного массива пойдет по адресу, равному адресу из первой размерности +1. И так далее:
 
Код:
10101010 -> 1-е значение;
10101011-> 2-е значение;

10101019 -> 10-е значение.

Вот и получается два последовательных массива. И ты, как раз, проверяя [0,9] и [1,0], попал «между двумя массива», а правильнее сказать – в область памяти, не принадлежащую ни одному из выделенных массивов.

В общем же случае после первого массива указателей выделяется второй массив указателей и т. д. И лишь предпоследний массив указывает на реальные данные.

Цитата: kosfiz
...дело в том, что такая "особенность" может повлиять на использование динамических массивов в некоторых случаях...


Это не особенность, по-другому динамический массив не выделишь, и не на что это не повлияет, так как всем этим занимается компоновщик. Другими словами, программа знает как перевести написанные тобой строки с языке высокого уровня в машинные инструкции и не запутаться в этом.:)

303
22 мая 2008 года
makbeth
1.0K / / 25.11.2004
Alfá, ну и? Почему промежутки то? :)
257
22 мая 2008 года
kosfiz
1.6K / / 18.09.2005
Alfá, что и как выделяется, что и как называется я знаю, поэтому и вопросы были:
[quote=kosfiz]
но если данный "пробел" и вправду есть то всвязи с чем была выбрана такая реализация, на ваш взгляд? или может быть это нужно для выполнения какой-либо цели или несет какую-то функцию, уж не знаю?
[/quote]
к тому же как и написал makbeth ты не объяснил наличие "пустот". видишь ли твое объяснение, на мой взгляд, никак не объясняет то, что есть такие "промежутки". ведь этот первый массив указателей, может быть таким, исходя из твоего пример:
 
Код:
10101010    10101020 и т.д.

и будь так - не было бы моего вопроса.
[QUOTE=Alfá;246019]и не на что это не повлияет, так как всем этим занимается компоновщик.[/QUOTE]
ну как сказать не повлияет. если бы я использовал статический массив, то при необходимости мог бы сослаться на него как на область памяти содержащую элементы определенного типа, а с динамическим массивом так не проканает, как раз из-за "пустот", и мне придется переходить от работы с массивом к работе с указателями.
9.4K
22 мая 2008 года
AIGrifon
165 / / 13.11.2007
По-моему пустоты нужны для более быстрой обработки. Слышал о таком факте, что быстрее обрабатываются данные, имеющие размер машинного слова, то есть, если слово имеет размер 2 байта, то данные в 1 байт стремятся дополнить до размера слова.
В качестве примера можно привести структуры, в которых для выравнивания используются битовые поля (для С). В Pascal'е запись выравнивается изначально, а чтобы отказаться от этого можно использовать packed record - экономим место, но теряем время.
Аналогично и с массивом - как сказано ранее, его размер округляется до 16. Видимо так быстрее работает.
16K
22 мая 2008 года
Alfá
59 / / 12.01.2007
Не, ну почему имеются «пустоты» - это вопрос к диспетчеру куч. Единственное могу сказать (написать), что предположения, что они (пустоты) нужны для возможного будущего увеличения массива неверны. Те ячейки, в которых будет хранится информация, действительно обнуляются перед тем как ты, программист, занесешь туда данные. Почему ты при переходе от [0,9] к [1,0] увидел «пустоту» (я понимаю, что под этим подразумевается ноль), можно ответить с двух позиций.
1. Ты увидел ноль, потому что, как я писал выше, системы резервирует, а, значит, и обнуляет двойное слово. 9 находится в середине третьего двойного слова. После него ты перешел к 3-му байту в 3-ем двойном слове, а это ноль. 2. Если изначально ты установил размерность, кратную 4, то такого бы не было. Допустим, при переходе от [0,11] к [1,0] ты бы вышел за границу массива и считал бы вообще неизвестно что (и это, кстати сказать, тоже было бы удивлением).
Так с чего я начал? :)Куда меня понесло? :rolleyes:А, так вот эти два ответа, особенно второй показывают, что «пустоты» не для возможного увеличения массива. Ну а почему не последовательно, повторюсь: к диспетчеру куч. Единственное могу предположить, основываясь на следующем: гранулярность страниц - 64К, гранулярность же выделенной диспетчером куч памяти – 8К. Возможно, система по своим канонам ищем места в выделенной памяти с выравниванием 8К и длиной, достаточной для занесения данных указанного тобой типа.
Цитата: kosfiz
ну как сказать не повлияет. если бы я использовал статический массив, то при необходимости мог бы сослаться на него как на область памяти содержащую элементы определенного типа, а с динамическим массивом так не проканает, как раз из-за "пустот", и мне придется переходить от работы с массивом к работе с указателями.


Ссылаясь на область памяти, ты все равно это укажешь как darr[i,j] (ведь мы говорим о Delphi, а не об ассемблере). Так эта запись вмещает в себя вариант и статического и динамического массива. А то что ты, работая с динамическим, работаешь с указателями, так это скрывает от тебя система.

257
23 мая 2008 года
kosfiz
1.6K / / 18.09.2005
[quote=Alfá]Ссылаясь на область памяти, ты все равно это укажешь как darr[i,j] (ведь мы говорим о Delphi, а не об ассемблере). Так эта запись вмещает в себя вариант и статического и динамического массива.[/quote]
я тебе говорю не о том, чтобы сослаться на определенный элемент, находящийся в памяти, а о том, чтобы сослаться на область памяти содержащую все элементы массива сразу. с динамическим такое не пройдёт.
276
23 мая 2008 года
Rebbit
1.1K / / 01.08.2005
Ну вопервых kosfiz у тебя не двумерный динамический масив, а динамический масив динамических масивов. Согласись вещи разные.
Насколько я помню нет такого как двумерный динамический масив в Делфи. Есть токо одномерные. Масив первого уровня есть масивом указателей на масивы второго уровня.
Динамический масив есть куском памяти в куче и выделится она может где угодно.

Если я ниче не путаю. (проверить неначем) такой псевдодвумерный масив не обязан быть ровным и вполне возможно сделать так
 
Код:
SetLength(darr, 3);
SetLength(darr[0], 10);
SetLength(darr[1], 11);
SetLength(darr[2], 12);
Дыры могут быть вызваны тем что память выделяется блоками, кроме того может Делфа думает что ты можеш увеличивать масивы второго уровня и оставляет там больше памяти чтоб не переписывать (но это не факт).
Да и вобще подозреваю что если ты напишеш гдето дальше по коду
 
Код:
SetLength(darr[1], 200);
То между darr[0,9] и darr[2,0] будет большая дыра, а кусок darr[1,*] улетит в другое место кучи.

Но, как я уже сказал, проверить негде. Может и загоняюсь.

Цитата: kosfiz
сослаться на область памяти содержащую все элементы массива сразу. с динамическим такое не пройдёт.


Как говорит один мой знакомый "А липка не спопнется" :)
Такой масив удобно использовать когда меняется его розмер (количество масивов второго уровня и длинна каждого из них). Если надо ровный двухмерный динамический масив в одном куске памяти и подряд - тогда делай динамический одномерный масив и сам мапай 2 индекса в 1. тогда все лежит вместе но так просто размер не изменить

257
23 мая 2008 года
kosfiz
1.6K / / 18.09.2005
Цитата: Rebbit
Ну вопервых kosfiz у тебя не двумерный динамический масив, а динамический масив динамических масивов. Согласись вещи разные.


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

Цитата: Rebbit

Если я ниче не путаю. (проверить неначем) такой псевдодвумерный масив не обязан быть ровным и вполне возможно сделать так
 
Код:
SetLength(darr, 3);
SetLength(darr[0], 10);
SetLength(darr[1], 11);
SetLength(darr[2], 12);


не путаешь, можно хоть ромбиком, хоть треугольничком сделать :)

Цитата: Rebbit

Если надо ровный двухмерный динамический масив в одном куске памяти и подряд - тогда делай динамический одномерный масив и сам мапай 2 индекса в 1. тогда все лежит вместе но так просто размер не изменить


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

мне интересно было вследствие чего получается так, как есть - любознательный я человек, а почитать об этом я не нашел где, так почему не спросить?! :) кто-то объяснит, кто-то ликбез решит устроить :) - главное из всего полученная информация ;)

276
23 мая 2008 года
Rebbit
1.1K / / 01.08.2005
Цитата: kosfiz
вот и я о том же, видишь двумерный использовать не получится


Но согласись, это изза спецыфики твоего задания. Просто тебе надо роботать с такими данными как с куском памяти. А в общем такое поведение этого масива вполне оправдано, а если Делфа еще и выделяет больше памяти для возможности розшырения масива без перезаписи в новый кусок памяти, то даже разумно.

257
23 мая 2008 года
kosfiz
1.6K / / 18.09.2005
Rebbit меня всегда динамические массивы устраивали целиком и полностью, поэтому соглашусь с тобой.
261
23 мая 2008 года
ahilles
1.5K / / 03.11.2005
[QUOTE=Alfá;246191]
1. Ты увидел ноль, потому что, как я писал выше, системы резервирует, а, значит, и обнуляет двойное слово. 9 находится в середине третьего двойного слова. После него ты перешел к 3-му байту в 3-ем двойном слове, а это ноль. 2. Если изначально ты установил размерность, кратную 4, то такого бы не было. Допустим, при переходе от [0,11] к [1,0] ты бы вышел за границу массива и считал бы вообще неизвестно что (и это, кстати сказать, тоже было бы удивлением).[/QUOTE]
я сам лично тестил!!! у меня промежутки были более чем 1500 байт!
Моя теория: Допустим есть дин. массив 7х9 сначала выделяется 7 массивов по 9 элементов. Потом выделяется ещё один массив из 7 элементов в ячейки, которого заносятся указаетли на массивы. а памяти выделяется больше из за гранулярности выделения (в моём случае было 16 байт).
16K
23 мая 2008 года
Alfá
59 / / 12.01.2007
Цитата: Rebbit
а если Делфа еще и выделяет больше памяти для возможности розшырения масива без перезаписи в новый кусок памяти


Не выделяется для расширения. Перезапись проводится. Правильно написал ahilles:а памяти выделяется больше из за гранулярности выделения.

1
24 мая 2008 года
kot_
7.3K / / 20.01.2000
Для чтения блоков из динамически веделенной для процесса памяти (кучи) необходимо использовать HeapWalk, предварительно проведя дефрагментацию и уплотнение кучи (соответствующими функциями). Возникновение промежутков связано с тем что память кучи одна для потока - рамеры объектов которые там создаються - различны. Если надо что бы это было по другому - надо создавать кучу самому или использовать проекцию в память.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог