Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Можно ли задавать размер динамического массива


Автор: poiuyt1234 17.2.2009, 20:39
Пишу прогу. Одна из ее функций анализирует текст. Ищет по определенным условиям слова. Если находит слово заносит его в ячейку массива. Так вот. Слов может быть от 0 до 1000000. Создавать массив с запасом наверно не стоит.  А лучше динамически. Но тогда как установить размер массива? При каждом обнаружении нужного слова счетчик будет увеличиваться на еденицу и это слово надо занести в массив.
Можно ли при каждой итерации(обнаружении нужного слова) цикла увеличивать размерность динамического массива на еденицу с помощью setlength ,без потери ранее записанных в него данных?

Автор: SneG0K 17.2.2009, 20:53
Цитата(poiuyt1234 @  17.2.2009,  19:39 Найти цитируемый пост)
увеличивать

Да. Предыдущие данные не теряются.

Автор: Poseidon 18.2.2009, 01:09
Цитата(poiuyt1234 @  17.2.2009,  20:39 Найти цитируемый пост)
Можно ли при каждой итерации(обнаружении нужного слова) цикла увеличивать размерность динамического массива на еденицу с помощью setlength ,без потери ранее записанных в него данных?
Изменять размерность массива в большую сторону можно без потери данных. В меншую - нет. Только стоит заметить, что выделение памяти - это очень ресурсоемкая и медленная задача. И если ты на каждую итерацию цикла будешь вызывать setlength, это может оказаться гораздо "тыжелее" и медленнее, чем если бы ты сразу выделил 1000000. Я бы увеличивал размерность блоками. Допустим, по 1000 единиц. Размер блока во многом зависет от того, что будет хранится в массиве. Если сеществует большая вероятность того, что практически все 1000000 будут использованы, то лучше сразу выделять память для 1000000 элементов массива. Если большая вероятность, что элементов будет не более 500000, увеличивать размеронсть по 500000 и т.п. В любом случае, выделить память для 10000 элементов гораздо быстрее, чем 10000 раз выделять память по одному элементу. А вообще, само правильно было бы пронализировать входные данные и выделить ровно столько, сколько надо. Для поставленной задачи это значит "посчитать сколько слов и выделить ровно такое количество элементов". В любом случае будет быстрее, чем делать setlength для каждого слова.

Автор: Virtuals 18.2.2009, 07:42
poiuyt1234, 

в дополнение к 
Poseidon, в двух словах.
setlength
1. создает новую переменную требуемого размера
2. копирует в нее содержимое предыдущей переменной
3. уничтожает старую.

соответственно для переменной размером в 1000 при setlength 2000? потребуется минимум 1000+2000=3000!!! не забываем об этом.

Автор: Alexeis 18.2.2009, 10:47
Цитата(Virtuals @  18.2.2009,  06:42 Найти цитируемый пост)
setlength
1. создает новую переменную требуемого размера
2. копирует в нее содержимое предыдущей переменной
3. уничтожает старую.


Все не так просто как кажется, чтобы в этом убедиться можно выполнить следующий код
Код

procedure TForm1.FormCreate(Sender: TObject);
var
 c : array of integer;
 p1, p2, p3 : Pointer;
begin
 setLength(c, 100);
 p1 := c;
 setLength(c, 50);
 p2 := c;
 c[49] := 1010101;
 setLength(c, 58);
 p3 := c;
 c[57] := 1010101;
 showmessage(IntToHex(Integer(p1), 8) + ' ' +
             IntToHex(Integer(p2), 8) + ' ' +
             IntToHex(Integer(p3), 8));
end;


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

  В результате мы имеем, то что увеличение размера массива при помощи setLength не будет приводить к перераспределению памяти, а функции High() и Length() будут давать правильный (а не зарезервированный размер). 

Автор: Christoph 18.2.2009, 15:29
Чуть не вкурил,Зачем выделять память побольше? если при нехватки памяти она сама выделить сколько ей надо, ну например выделили память 
Код

SetLength(a,1000);

И это получается что если я туда запишу какое то одно число, а остальная память будет гулять, потому получается что эту память может использоваться массив, хотя могу и ошибаться 
И например если написать так
Код

SetLength(a,10);
a[50]:=10;
 
Так зачем переживать за память? если присвоится без проблем

И еще вот что из кода Alexeis, p1,p2,p3 - выводят начальный адресс массива, я прав?

Автор: Snowy 18.2.2009, 15:50
Поясняю на пальцах:
1. Создаём массив a, размером 1
Получаем в памяти: [a][свободное место]
Дальше в работе нам понадобилась переменная i
Имеем в памяти: [a][i][свободное место]
Теперь мы хотим увеличить массив а до 2.
Но вот проблема - место за [a] уже занято. Значит, чтобы увеличить, нужно перенести весь массив [а] туда, где место есть.
То есть [ ][i][a ][свободное место]
Если массив [a] имеет большой размер, то выделение 1 места приведёт к копированию всего массива, что при большом размере довольно чувствительно.
Однако, не вижу особых причин этого боятся в целом алгоритме.
При обработке данных в цикле, такое перераспределение произойдёт всего 1 раз, пока массив маленький.
Если мы не выделяем постоянно память, то вероятность дальнейших переносов невелика.
Хотя, если выделить сразу большой блок, то вовсе исключается.
Выделение большого блока, с последующим уменьшением тоже вариант, но тоже не гарантия, хотя снижает количество перераспределений.
Впрочем, если счёт идёт не на десятки мегабайт в процессе, чувствительном к задержке в пару-тройку миллисекунд, то я бы и заморачиваться не стал.
Всё равно в фиксированном цикле перераспределений немного, а в нефиксированом частота перераспределений невысока.
Для массивов до мегабайта это не чувствительно.
Поскольку речь идёт о массиве, предельный размер которого 4Мб, то не стоит и голову ломать.
На фоне основного алгоритма, перераспределение будет абсолютно нечувствительно.

Автор: Alexeis 18.2.2009, 15:56
Цитата(Christoph @  18.2.2009,  14:29 Найти цитируемый пост)
И еще вот что из кода Alexeis, p1,p2,p3 - выводят начальный адресс массива, я прав?

  Указатель на память массива. Поскольку значение адреса не меняется, то память не перераспределялась. Тут видно что если перваночально памяти было выделено много, то последующие уменьшения и увеличения размера не приводят к перераспределению, пока хватает памяти.

Автор: bems 18.2.2009, 17:41
Alexeis, а если так попытаться работать с объемом больше 64-х кило, оно так же работает?

Автор: poiuyt1234 18.2.2009, 17:58
а обрезать лишние ячейки тож можно без потери данных в заполненных ячейках с помощью setlength? т.е. понадобилось фактически  700000 из 1000000, а потом setlength(a,700000)

Автор: Riply 18.2.2009, 17:58
Цитата(Alexeis @  18.2.2009,  15:56 Найти цитируемый пост)
Поскольку значение адреса не меняется, то память не перераспределялась. Тут видно что если перваночально памяти было выделено много, то последующие уменьшения и увеличения размера не приводят к перераспределению, пока хватает памяти. 


Я бы, наверное, не рискнула быть столь категоричной.

SetLength(a, 1000000);
SetLength(a, 1);
// Здесь "система" считает хвостик свободным и вольна поступать с ним как ей хочеться
//... что-то происходит
SetLength(a, 10); - перераспределения не произошло, если в "//..." 
хвостик "системой" не был использован, что может зависеть от расположения звезд smile


Автор: Christoph 18.2.2009, 17:59
Alexeis, Snowy,  спасибо за разъяснения, если кто то может киньте ссылку про менеджер памяти в делфи

Автор: cemick 18.2.2009, 18:08
Цитата(Christoph @  18.2.2009,  17:59 Найти цитируемый пост)
если кто то может киньте ссылку про менеджер памяти в делфи

вроде менеджер памяти тут не причем(вроде как), все делается в DynArraySetLength в System..

Автор: Alexeis 18.2.2009, 18:28
Riply, обычная схема предполагает, что раз один раз понадобилось, то может понадобиться еще раз. Делфийский менеджер памяти не очень любит отдавать память, а при уменьшении размера массива он и вовсе не отдает. Это общая стратегия выделения памяти. Гарантировать такое поведение нельзя, но наедятся можно.

Автор: Christoph 18.2.2009, 18:33
Цитата(cemick @ 18.2.2009,  18:08)
Цитата(Christoph @  18.2.2009,  17:59 Найти цитируемый пост)
если кто то может киньте ссылку про менеджер памяти в делфи

вроде менеджер памяти тут не причем(вроде как), все делается в DynArraySetLength в System..

Да, но мне интересно почитать )

Прочитал пост Riply и написал код, вроде тот хвостик никто не трогает, хотя немного не уверен 

Код

procedure TForm1.FormCreate(Sender: TObject);
var Int: PInteger;
    arr: array of integer;
    p1,p2:Pointer;
begin
  SetLength(arr,15);
  arr[5]:=2;
  p1:=arr;
  SetLength(arr,10);
  New(Int);
  Int^:=1000;
  arr[13]:=9898;
  p2:=arr;
  showmessage(IntToHex(Integer(p1), 8) + ' ' +
               IntToHex(Integer(p2), 8)) ;
   dispose(Int);
end;



Alexeis, ответил уже  smile 

Автор: Riply 18.2.2009, 18:46
Цитата(Alexeis @  18.2.2009,  18:28 Найти цитируемый пост)
Это общая стратегия выделения памяти


Полностью согласна.


Цитата(Alexeis @  18.2.2009,  18:28 Найти цитируемый пост)
Гарантировать такое поведение нельзя, но наедятся можно. 


Дык, под "зависит от расположения звезд" я и имела ввиду,  что нам остается только надеяться smile

Автор: Bose 18.2.2009, 18:51
Цитата(Christoph @  18.2.2009,  16:59 Найти цитируемый пост)
если кто то может киньте ссылку про менеджер памяти в делфи 


Посмотри пост в блоге http://vingrad.ru/users/CodeMonkey:
http://gunsmoker.blogspot.com/2009/01/blog-post.html

и обязательно пройдись по ссылочкам из этого поста.

Автор: Christoph 18.2.2009, 19:03
Bose, спасибо  smile 

Автор: Uridian 19.2.2009, 19:20
Цитата(poiuyt1234 @  17.2.2009,  20:39 Найти цитируемый пост)
Пишу прогу. Одна из ее функций анализирует текст. Ищет по определенным условиям слова. Если находит слово заносит его в ячейку массива. Так вот. Слов может быть от 0 до 1000000. Создавать массив с запасом наверно не стоит.  А лучше динамически. Но тогда как установить размер массива? При каждом обнаружении нужного слова счетчик будет увеличиваться на еденицу и это слово надо занести в массив.
Можно ли при каждой итерации(обнаружении нужного слова) цикла увеличивать размерность динамического массива на еденицу с помощью setlength ,без потери ранее записанных в него данных? 

Почему бы не использовать для хранения найденных слов объект класса TStringList вместо динамического массива?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)