PDA

Просмотр полной версии : Освобождение выделанной памяти



Esgal
28.03.2009, 22:11
Допустим, мне дан довольно большой файл (count строк, но не больше 100000), состоящий из строк произвольной длинны (допустим до 200)

Я выделяю память под массив и под строки



const count=32000;
type PStr=^string;
Arr=array[1..1] of PStr;
PArr=^Arr;
var buf:string;
i:1..100000;
DArr:PArr;
begin
...
{Тут было согласование файла f}
...
GetMem(DArr,Count*SizeOf(PStr));
for i:=1 to count do
begin
readln(f,buf);
GetMem(DArr^[i],length(buf)+1);
DArr^[i]^:=buf;
end;
...
{теперь я работаю с полученной структурой, и когда работа заканчивается, приходит время освобождать память}
...
end.


Два вопроса

1)Правильно ли я всё сделал;
2)Как теперь проще всего освободить память?
Неужели придётся заново проходить оп массиву, проверять размер каждой из строк, и выгружать каждую строку отдельно?

Naeel Maqsudov
28.03.2009, 23:05
1) Формально все правильно, но может быть сам подход неправильный? Вцелом задача какова?

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

Подумайте, может стоит отказаться от хранения string в динамическом контексте и перейти на PChar?

Esgal
28.03.2009, 23:49
Подумайте, может стоит отказаться от хранения string в динамическом контексте и перейти на PChar?

как это на Pchar

объясните пожалуйста если не затруднит...

задача:
файл состоит из n+1 строк
в первой строке файла число n (1<=n<=100000)
В n следующих срок, строки произвольной длинны

Нужно считать строки со 2 по n+1 в память, при этом нужно занять минимальное количество памяти
--------------------------------------------------------------------------------
Добавленное сообщение
--------------------------------------------------------------------------------
я тут поэксперементировал

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



GetMem(DArr,Count*SizeOf(PStr));
for i:=1 to count do
begin
readln(f,buf);
{раньше в этой строке я выделял память под строку}
DArr^[i]^:=buf;
end;


этот код работает... строки из массива DArr^[i]^ прекрасно выводятся на экран, кто объяснить суть явления?
--------------------------------------------------------------------------------
Добавленное сообщение
--------------------------------------------------------------------------------
Извените за много сообщений, оказывается вышеприведённый код работает только для маленького числа строк (и всё равно непонятно почему)

Naeel Maqsudov
29.03.2009, 10:50
То что этот код работает это просто счастливая случайность. Так как DArr^[i] - это случайное число. Если его расмотреть как указатель, то оно указывает на случайное место в памяти (а не на место, которое было специально выделено).
Следовательно, DArr^[i]^:=buf; затирает какие-то данные где-то в памяти.
Т.е. даже если это работет, это грубейшая ошибка.


как это на Pchar

На какой версии Pascal вы пишете? Начиная с 7 есть специальные функции, для работы с ASCIIZ (null-terminated) строками. Они сами обеспечивают уборку мусора. И есть стандартный тип PChar - указатель на ASCIIZ-строку.

Если используется Pascal 5, то обратите внимание на стандартные процедуры
Mark(p) и Release(p)
Запомнив состояние "кучи" по Mark можно одной командой Repease привести ее в исходное сотояние, освободив всё, что выделялось.


задача:
файл состоит из n+1 строк
в первой строке файла число n (1<=n<=100000)
В n следующих срок, строки произвольной длинны
Нужно считать строки со 2 по n+1 в память, при этом нужно занять минимальное количество памяти

Ну размер файла Вы знаете (можете определить), не так ли?
Выделите память одним куском. Считайте весь файл целиком, не тратя память на указатели и фрагментирование памяти.

Дальше можно превратить этот текст как в набор строк, идущих подряд.
Если заменить переводы строк нулями, то получатся ASCIIZ строки.
Если вставить перед каждой строки ее длину, то LASCII, соответствующие паскалевскому String. (Когда первый байт - это длина).

atavin-ta
30.03.2009, 11:18
)Как теперь проще всего освободить память?
Неужели придётся заново проходить оп массиву, проверять размер каждой из строк, и выгружать каждую строку отдельно?

for i:=1 to count do
FreeMem(DArr^[i]);

BBB
30.03.2009, 13:01
К слову, если писать на Паскале, то оператор:
GetMem(DArr,Count*SizeOf(PStr));
не прокатит при (Count >= 16384). Т.е. SizeOf(PStr) = 4, а не в 32-битных приложениях нельзя выделить кусок памяти, превышающий размером 64 К.
Не скаже, что именно произойдет в данном случае: или произведение (Count*SizeOf(PStr)) по переполнению даст небольшой результат, и память выделится, но явно меньшего размера, или GetMem даст ошибку. Вообще, надо смотреть тип параметра GetMem. Если это word, то явно должно произойти переполнение его значения.

Esgal
30.03.2009, 20:05
BBB, я тоже думал об этом...

А есть ли вообще способ, адресовать 100 000 строк в Паскале?
--------------------------------------------------------------------------------
Добавленное сообщение
--------------------------------------------------------------------------------
atavin-ta, резвеу freemem второй параметр не обязателен?

atavin-ta
31.03.2009, 09:39
резвеу freemem второй параметр не обязателен?
Точно не помню, но мо-моему нет.

Naeel Maqsudov
31.03.2009, 15:27
у freemem второй параметр не обязателен?

Параметр обязателен, что кажется нелогичным. Ведь кучей распоряжается все равно внутренний менеджер кучи.... Надо смотреть system.pas....


есть ли вообще способ, адресовать 100 000 строк

Если целым куском нельзя, то надо более мелкими кусками.
Связанные списки, например.
Или (если не хочется дробить данные так мелко) кусками по чуть меньше 64К, но надо будет обработать пограничные условия. Т.е. индекс элемента должен пересчитываться в номер куска и индекс элемента в куске.

atavin-ta
01.04.2009, 05:55
Если целым куском нельзя, то надо более мелкими кусками.
Связанные списки, например.
Или (если не хочется дробить данные так мелко) кусками по чуть меньше 64К, но надо будет обработать пограничные условия. Т.е. индекс элемента должен пересчитываться в номер куска и индекс элемента в куске.
Согласен, но только в том случае, когда целиком действительно нельзя или нецелесообразно. И кто сказал, что блок может быть не более 64 к? Можно использовать и 32 разряда.

BBB
01.04.2009, 09:24
BBB, я тоже думал об этом...
А есть ли вообще способ, адресовать 100 000 строк в Паскале?[QUOTE]Списком, как уже упомянул Naeel Maqsudov.
[QUOTE]Разве freemem второй параметр не обязателен? В Turbo/Borland PAscal обязателен. Это в C этот параметр у аналогичной функции отсутствует.

somewhere
02.04.2009, 10:53
Во-первых, сильно сомневаюсь, что средний размер строки будет порядка 5 символов. Как бы мы не выделяли память, то общий адресуемый свободный кусок будет не более 500К, т.е. за вычетом резидентов, системных таблиц и пр. Как ни крути, но придется задействовать XMS или EMS драйвер для доступа к расширеной памяти, и осуществлять доступ к нужной строке через функцию, которая будет сама управлять подкачкой новых блоков и правкой индексов, как сказал Naeel Maqsudov.
Во-вторых, выделить память заранее для N строк, как было предложено не получится хотя бы потому, что строки имеют разную длину.
И даже создать единую таблицу поинтеров на них будет нерационально, в 64К они явно не поместятся. Однако имеет смысл хранить таблицу вида
[номер_блока_XMS]
[колво_строк_в_блоке]
А в самом блоке локальную таблицу смещений для каждой строки. Это позволит значительно ускорить обращение к нужной строке по сравнению со связаным списком, если конечно не считать времени для подкачки нужного блока из памяти (однако в связаном списке таких подкачек будет еще больше при обращении к строкам, индекс которых далеко от 0). Разумеется должна быть проверка есть ли такой блок уже подкаченный или нет.
Такая таблица кстати может кому то напомнить аппаратную связь GDT/LDT

alexander.spb
02.04.2009, 11:52
А действительно ли нужно считывать в RAM весь файл?
Если без этого никак -
- определите размер файла;
- выделите блок памяти этого размера;
- считайте его в RAM и обработайте как массив байтов.