вопрос по размещению элементов динамического массива в памяти
[highlight=delphi]
var
darr:array of array of byte;
{....................................}
SetLength(darr, 3, 10);
[/highlight]
то казалось бы за элементом darr[0, 9] в памяти должен идти darr[1, 0], однако если посмотреть то нет, в памяти между ними как бы пусто, т.е. они не идут друг за другом.
отсюда вопрос: может я все-таки ошибаюсь? но если данный "пробел" и вправду есть то всвязи с чем была выбрана такая реализация, на ваш взгляд? или может быть это нужно для выполнения какой-либо цели или несет какую-то функцию, уж не знаю? поделитесь вашим мнением по данному вопросу, очень интересно.
тогда интересно, что мешает выделить память сразу под все элементы?! дело в том, что такая "особенность" может повлиять на использование динамических массивов в некоторых случаях и тем самым лишить удобства разработки, неужели об этом не задумывались создатели. к тому же очень интересно то, что эти "пустоты" имеют одинаковый размер и их "протяженность" меняется в зависимости от типов элементов.
я тут немного потестил и убедился что именно так и есть: массив указателей на массивы байтов. Располагаются они не посдедовательно, и размер кадого подмассива округляется до числа кратного 16. (у меня была матрица 5х5, первые три массива были рядом, ещё два отдельно)
по-моему создатели рукводоствовались правилом: найти большой свободный участок сложнее, чем найти множество маленьких. Тем более если есть много маленньких свободных участков, то выделение одного большого, будет расточительно, тем более если у нас массив большой размерности.
немного странно или может я чего не понимаю. когда мы объявляем статический массив(такой же размерности), то элементы располагаются последовательно,т.е. выделяется "большой"(в моем случае в 30 байт, невероятно большой:)) участок - здесь они уже своим "правилом" не пользуются? по меньшей мере странно и как я уже упоминал вносит некоторые неудобства. оправдано ли это - вопрос спорный.
kosfiz, смотри, как работает выделение динамического массива. Ну, на то он и динамическим называется, что память под него распределяется в ходе выполнения программы. Для твоего примера с двумерным массивом выделение происходит дважды. Для общего случая – столько, какова размерность многомерного массива.
Конкретно для твоего примера, а общий случай будет почти аналогичным. Сперва выделяется массив указателей. Не для того, чтобы выставить тебе незнающим, тупым и т.д., а просто для логики размышления и для понимания другими читающими, напомню, что указатель – это переменная, которая в качестве своего значения хранит адрес другой ячейки памяти, где действительно находятся данные.
Так вот, выделяется массив указателей. Он последовательный. Фактически - это размерность по первому измерению твоего исходного массива. Ну а так как система 32-хразрядная, то выделится 3 двойных слова. К примеру, это такие слова:
Данные же заносятся во второй массив, но он, как таковой не выделяется. Другими словами: машина берет первый массив и по адресу, указанному в нем, заносит само значение. Второе значение из твоего исходного массива пойдет по адресу, равному адресу из первой размерности +1. И так далее:
10101011-> 2-е значение;
…
10101019 -> 10-е значение.
Вот и получается два последовательных массива. И ты, как раз, проверяя [0,9] и [1,0], попал «между двумя массива», а правильнее сказать – в область памяти, не принадлежащую ни одному из выделенных массивов.
В общем же случае после первого массива указателей выделяется второй массив указателей и т. д. И лишь предпоследний массив указывает на реальные данные.
Это не особенность, по-другому динамический массив не выделишь, и не на что это не повлияет, так как всем этим занимается компоновщик. Другими словами, программа знает как перевести написанные тобой строки с языке высокого уровня в машинные инструкции и не запутаться в этом.:)
[quote=kosfiz]
но если данный "пробел" и вправду есть то всвязи с чем была выбрана такая реализация, на ваш взгляд? или может быть это нужно для выполнения какой-либо цели или несет какую-то функцию, уж не знаю?
[/quote]
к тому же как и написал makbeth ты не объяснил наличие "пустот". видишь ли твое объяснение, на мой взгляд, никак не объясняет то, что есть такие "промежутки". ведь этот первый массив указателей, может быть таким, исходя из твоего пример:
и будь так - не было бы моего вопроса.
[QUOTE=Alfá;246019]и не на что это не повлияет, так как всем этим занимается компоновщик.[/QUOTE]
ну как сказать не повлияет. если бы я использовал статический массив, то при необходимости мог бы сослаться на него как на область памяти содержащую элементы определенного типа, а с динамическим массивом так не проканает, как раз из-за "пустот", и мне придется переходить от работы с массивом к работе с указателями.
В качестве примера можно привести структуры, в которых для выравнивания используются битовые поля (для С). В Pascal'е запись выравнивается изначально, а чтобы отказаться от этого можно использовать packed record - экономим место, но теряем время.
Аналогично и с массивом - как сказано ранее, его размер округляется до 16. Видимо так быстрее работает.
1. Ты увидел ноль, потому что, как я писал выше, системы резервирует, а, значит, и обнуляет двойное слово. 9 находится в середине третьего двойного слова. После него ты перешел к 3-му байту в 3-ем двойном слове, а это ноль. 2. Если изначально ты установил размерность, кратную 4, то такого бы не было. Допустим, при переходе от [0,11] к [1,0] ты бы вышел за границу массива и считал бы вообще неизвестно что (и это, кстати сказать, тоже было бы удивлением).
Так с чего я начал? :)Куда меня понесло? :rolleyes:А, так вот эти два ответа, особенно второй показывают, что «пустоты» не для возможного увеличения массива. Ну а почему не последовательно, повторюсь: к диспетчеру куч. Единственное могу предположить, основываясь на следующем: гранулярность страниц - 64К, гранулярность же выделенной диспетчером куч памяти – 8К. Возможно, система по своим канонам ищем места в выделенной памяти с выравниванием 8К и длиной, достаточной для занесения данных указанного тобой типа.
Ссылаясь на область памяти, ты все равно это укажешь как darr[i,j] (ведь мы говорим о Delphi, а не об ассемблере). Так эта запись вмещает в себя вариант и статического и динамического массива. А то что ты, работая с динамическим, работаешь с указателями, так это скрывает от тебя система.
я тебе говорю не о том, чтобы сослаться на определенный элемент, находящийся в памяти, а о том, чтобы сослаться на область памяти содержащую все элементы массива сразу. с динамическим такое не пройдёт.
Насколько я помню нет такого как двумерный динамический масив в Делфи. Есть токо одномерные. Масив первого уровня есть масивом указателей на масивы второго уровня.
Динамический масив есть куском памяти в куче и выделится она может где угодно.
Если я ниче не путаю. (проверить неначем) такой псевдодвумерный масив не обязан быть ровным и вполне возможно сделать так
SetLength(darr[0], 10);
SetLength(darr[1], 11);
SetLength(darr[2], 12);
Да и вобще подозреваю что если ты напишеш гдето дальше по коду
Но, как я уже сказал, проверить негде. Может и загоняюсь.
Как говорит один мой знакомый "А липка не спопнется" :)
Такой масив удобно использовать когда меняется его розмер (количество масивов второго уровня и длинна каждого из них). Если надо ровный двухмерный динамический масив в одном куске памяти и подряд - тогда делай динамический одномерный масив и сам мапай 2 индекса в 1. тогда все лежит вместе но так просто размер не изменить
я с этим и не спорю, просто второй вариант длинно пишется, да и чаще всего все используют двумерный динамический массив, а сущность его не зависит от названия.
Если я ниче не путаю. (проверить неначем) такой псевдодвумерный масив не обязан быть ровным и вполне возможно сделать так
SetLength(darr[0], 10);
SetLength(darr[1], 11);
SetLength(darr[2], 12);
не путаешь, можно хоть ромбиком, хоть треугольничком сделать :)
Если надо ровный двухмерный динамический масив в одном куске памяти и подряд - тогда делай динамический одномерный масив и сам мапай 2 индекса в 1. тогда все лежит вместе но так просто размер не изменить
вот и я о том же, видишь двумерный использовать не получится, а если работать с одномерным, то сам понимаешь будут определенные неудобства, я так делать не стал, но опять же столкнулся с неудобствами.
мне интересно было вследствие чего получается так, как есть - любознательный я человек, а почитать об этом я не нашел где, так почему не спросить?! :) кто-то объяснит, кто-то ликбез решит устроить :) - главное из всего полученная информация ;)
Но согласись, это изза спецыфики твоего задания. Просто тебе надо роботать с такими данными как с куском памяти. А в общем такое поведение этого масива вполне оправдано, а если Делфа еще и выделяет больше памяти для возможности розшырения масива без перезаписи в новый кусок памяти, то даже разумно.
1. Ты увидел ноль, потому что, как я писал выше, системы резервирует, а, значит, и обнуляет двойное слово. 9 находится в середине третьего двойного слова. После него ты перешел к 3-му байту в 3-ем двойном слове, а это ноль. 2. Если изначально ты установил размерность, кратную 4, то такого бы не было. Допустим, при переходе от [0,11] к [1,0] ты бы вышел за границу массива и считал бы вообще неизвестно что (и это, кстати сказать, тоже было бы удивлением).[/QUOTE]
я сам лично тестил!!! у меня промежутки были более чем 1500 байт!
Моя теория: Допустим есть дин. массив 7х9 сначала выделяется 7 массивов по 9 элементов. Потом выделяется ещё один массив из 7 элементов в ячейки, которого заносятся указаетли на массивы. а памяти выделяется больше из за гранулярности выделения (в моём случае было 16 байт).
Не выделяется для расширения. Перезапись проводится. Правильно написал ahilles:а памяти выделяется больше из за гранулярности выделения.