Адрес, прописка…
Сегодня мы займемся адресной арифметикой в Делфи. Перестаньте так на меня смотреть я прекрасно знаю, что в Делфи нет адресной арифметики. В смысле, такой как в Си («сишники» посмеиваются). Можно присваивать один указатель другому, сравнивать их на равно/не равно и все. Нет сложения, вычитания указателей и прочих полезных вещей. Но, как говорил один мой преподаватель, если очень хочется, то можно. А я очень люблю эту среду разработки, так что всеми силами пытаюсь показать, что в ней можно все. В крайнем случае, если Делфи чего-то и не может, то она имеет BASM (Built-in-Assembler), а уж он точно круче всех. Так что возьмем пока его на вооружение.
О файлах и памяти
Многие не знают (я и сам не знал, пока мне не рассказали) об одной возможности при написании программ. Она очевидна, но пока не возникает потребность, никто об этом не задумывается. Я имею ввиду работу в файле «без типа». Суть в том, что наши объемные данные мы записываем в этот самый файл на винте, а не в оперативку. Если надо изменить какую-то часть, то читаем в память, изменяем и записываем обратно. Мы создаем как бы свою личную маленькую оперативную память. PhotoShop, например, так поступает. Но эту технологию оправданно применять лишь в том случае, если вам мало всей оперативной памяти, ведь винчестер работает во много раз медленнее оперативной памяти. Да и программировать становится труднее, ведь в случае с оперативкой мы не заботимся о выделении/освобождении динамической памяти GetMem, FreeMem и все. В случае же файла нам придется писать эти процедуры самим, создавать список дыр (свободных участков внутри файла), процедуры обслуживания этого списка. Создавать, в общем, механизмы управления памятью, которые уже реализованы в ОС.
Я писал однажды программу, работающую в файле. Труднее, конечно, чем в памяти, но написал. Что мне понравилось, так это возможность бродить по всему файлу. Можно спозиционироваться на любой байтик. Потом я вернулся в динамическую память. И для решения проблем, которые в файле решались легко (переместить указатель на четыре байта), в памяти пришлось применять другие способы. То есть мне не хватало возможностей адресной арифметики.
Поясню на примере.
Чтобы записать в файл значение какой-то переменной, нам кроме этой переменной ничего больше не нужно, ведь ее размер можно узнать с помощью функции SizeOf. Например, если a: integer, f: file, то BlockWrite(f, a, SizeOf(a)). Чтобы записать некоторый объект в памяти, нужно иметь указатель на этот объект и его размер:
Переменную a тоже можно записать с помощью указателя на нее:
Мне надо было написать универсальную процедуру записи в файл объектов (под словом объект я имею ввиду не тот объект, который основа ООП, а просто некоторую запись, например, как объявленную ниже):
Конечно, можно написать процедуру, в которую надо передавать адрес, ключевое слово (необходимо для сортировки) и размер. Но ведь эти данные хранятся в самой записи (первые и вторые четыре байта), так что мы будем передавать лишнее. Одним словом, процедуре надо передавать только адрес. Ключевое поле и размер она должна сама суметь прочитать. С ключевым полем все понятно, адрес на объект указывает прямо на него, поэтому легко можно прочитать так: prockey := integer(p). С полем размера тоже было бы аналогично, если бы нам сдвинуть указатель вверх на четыре байта (говоря «вверх», я имею ввиду «в сторону возрастания адресов»). Так что налицо необходимость адресной арифметики. Но Делфи рулит, поэтому можно обойтись без нее!
Адресная арифметика без изменения адресов
Сейчас я покажу, как можно прочитать вторые четыре байта, если указатель указывает на первые. Объявим еще такое:
Повесьте где-то на ButtonClick, чтобы проверить работоспособность.
Делфи рулит! Развить эту идею не составляет труда: используем массив не integer, а byte, получаем доступ к отдельным байтам, и так дальше. Как видите, без адресной арифметики вполне можно обойтись, но все же утрем сишникам нос и покажем, что в Делфи можно проводить те же операции с адресами.
Вариант 1. Асм
Пусть у нас есть переменная-указатель
Маленький пример кода, с помощью которого можно делать с этим указателем что угодно:
Все просто! Теперь вместо add подставляете то, что нужно вам и юзаете на здоровье! Приведу еще пример сложения двух указателей p1 и p2 в третий p:
Ассемблер рулит! Вот только сишники будут говорить, что ассемблер и Делфи разные вещи, так что реализованная выше адресная арифметика заслуга совсем не Делфи. Что ж, оставим асм в покое и реализуем первый пример чисто на Делфи.
Вариант 2. Без асма
Для этого нам потребуется еще пара переменных a: LongInt (4 байта, целое, беззнаковое) и указатель на переменные типа
LongInt: pInt: ^LongInt.
Вышло даже на целую строчку короче!
Так что если вы собрались переходить на Си из-за того, что не нашли в Делфи аналогичных возможностей работы с адресами, то не спешите, а лучше пошевелите мозгами, ибо Delphi the best!
|