Создаем дополнительную память на диске с помощью Delphi
06.04.09Мой Компьютер, №04 (435), 15.01.2007
О файлах и памяти
Многие не знают (я и сам не знал, пока мне не рассказали) об одной возможности при написании программ. Она очевидна, но пока не возникает потребность, никто об этом не задумывается. Я имею ввиду работу в файле «без типа». Суть в том, что наши объемные данные мы записываем в этот самый файл на винте, а не в оперативку. Если надо изменить какую-то часть, то читаем в память, изменяем и записываем обратно. Мы создаем как бы свою личную маленькую оперативную память. PhotoShop, например, так поступает. Но эту технологию оправданно применять лишь в том случае, если вам мало всей оперативной памяти, ведь винчестер работает во много раз медленнее оперативной памяти. Да и программировать становится труднее, ведь в случае с оперативкой мы не заботимся о выделении/освобождении динамической памяти — GetMem, FreeMem и все. В случае же файла нам придется писать эти процедуры самим, создавать список дыр (свободных участков внутри файла), процедуры обслуживания этого списка. Создавать, в общем, механизмы управления памятью, которые уже реализованы в ОС.
Я писал однажды программу, работающую в файле. Труднее, конечно, чем в памяти, но написал. Что мне понравилось, так это возможность бродить по всему файлу. Можно спозиционироваться на любой байтик. Потом я вернулся в динамическую память. И для решения проблем, которые в файле решались легко (переместить указатель на четыре байта), в памяти пришлось применять другие способы. То есть мне не хватало возможностей адресной арифметики.
Поясню на примере.
Чтобы записать в файл значение какой-то переменной, нам кроме этой переменной ничего больше не нужно, ведь ее размер можно узнать с помощью функции SizeOf. Например, если a: integer, f: file, то BlockWrite(f, a, SizeOf(a)). Чтобы записать некоторый объект в памяти, нужно иметь указатель на этот объект и его размер:
p: pointer;
size: integer;
BlockWrite(f, p^, size)
Переменную a тоже можно записать с помощью указателя на нее:
p := @a;
size := SizeOf(a);
BlockWrite(f, p^, size)
Мне надо было написать универсальную процедуру записи в файл объектов (под словом объект я имею ввиду не тот объект, который основа ООП, а просто некоторую запись, например, как объявленную ниже):
type
TSomeType = record
key, size: integer; //уникальное ключевое поле и размер
//другие поля
x: array[1..8] of byte
end;
var b: TSomeType
Конечно, можно написать процедуру, в которую надо передавать адрес, ключевое слово (необходимо для сортировки) и размер. Но ведь эти данные хранятся в самой записи (первые и вторые четыре байта), так что мы будем передавать лишнее. Одним словом, процедуре надо передавать только адрес. Ключевое поле и размер она должна сама суметь прочитать. С ключевым полем все понятно, адрес на объект указывает прямо на него, поэтому легко можно прочитать так: prockey := integer(p). С полем размера тоже было бы аналогично, если бы нам сдвинуть указатель вверх на четыре байта (говоря «вверх», я имею ввиду «в сторону возрастания адресов»). Так что налицо необходимость адресной арифметики. Но Делфи рулит, поэтому можно обойтись без нее!
Адресная арифметика без изменения адресов
Сейчас я покажу, как можно прочитать вторые четыре байта, если указатель указывает на первые. Объявим еще такое:
type
TArray12 = array[1..2] of integer;
var
m: TArray12;
Повесьте где-то на ButtonClick, чтобы проверить работоспособность.
//заполняем поля key и size
b.key := 512;
b.size := 1024;
//формируем указатель на эту запись
p := @b;
//читаем первые восемь байт в массив
m := TArray12(p^);
// и выводим второй элемент массива — поле size
ShowMessage(IntToStr(m[2]));
Делфи рулит! Развить эту идею не составляет труда: используем массив не integer, а byte, получаем доступ к отдельным байтам, и так дальше. Как видите, без адресной арифметики вполне можно обойтись, но все же утрем сишникам нос и покажем, что в Делфи можно проводить те же операции с адресами.
Вариант 1. Асм
Пусть у нас есть переменная-указатель
p: pointer
Маленький пример кода, с помощью которого можно делать с этим указателем что угодно:
asm
push eax
//сохраним eax на всякий случай
mov eax, p
//заносим в eax значение нашего указателя
//и делаем с ним что-нибудь, например, увеличиваем на 4
add eax, 4
//по сути, мы сдвинули указатель на 4 байта в сторону увеличения адресов
mov p, eax
//записываем новое значение указателя обратно в память
//и восстанавливаем eax
pop eax
end; //asm
Все просто! Теперь вместо add подставляете то, что нужно вам — и юзаете на здоровье! Приведу еще пример сложения двух указателей p1 и p2 в третий p:
asm
push eax
push ebx
//сохраняем изменяемые регистры
mov eax, p1
mov ebx, p2
//заносим в регистры значения слагаемых указателей
add eax, ebx
//суммируем и записываем в третий указатель
mov p, eax
pop ebx
pop eax
//восстановили регистры
end; //asm
Ассемблер рулит! Вот только сишники будут говорить, что ассемблер и Делфи — разные вещи, так что реализованная выше адресная арифметика — заслуга совсем не Делфи. Что ж, оставим асм в покое и реализуем первый пример чисто на Делфи.
Вариант 2. Без асма
Для этого нам потребуется еще пара переменных — a: LongInt (4 байта, целое, беззнаковое) и указатель на переменные типа LongInt: pInt: ^LongInt.
pint := @p;
//получили адрес в памяти, где хранится значение нашего указателя
a := pint^;
//прочитали это значение
a := a + 4;
//изменили
pInt^ := a;
//и записали обратно
Вышло даже на целую строчку короче!
Так что если вы собрались переходить на Си из-за того, что не нашли в Делфи аналогичных возможностей работы с адресами, то не спешите, а лучше пошевелите мозгами, ибо Delphi — the best!
Ярик Уланович aka Mahpella
Web-droid редактор
вологість:
тиск:
вітер:
Обзор умных часов BlackView W60: месяц в защите
Когда казалось, что китайские производители уже не могут удивить нас оснащением за низкую цену, появляются смарт-часы BlackView W60 с огромным аккумулятором, фонариком и вменяемым интерфейсом
Коврик для мыши с RGB-подсветкой Razer Firefly V2 Pro оснащен двумя портами USB
Razer мышкаRazer Firefly V2 Pro поставляется с покрытием, которое сокращает время отклика мыши, а также оборудован кабелем и двумя портами: USB-C и USB-A
Игровые ноутбуки Gigabyte Aorus 16X и G6X уже продаются в Украине
Gigabyte ноутбук события в УкраинеПредставительство компании Gigabyte в Украине анонсировало старт продаж игровых ноутбуков Aorus 16X и G6X.