000176. Выделение памяти

Всем привет! В этом уроке мы ознакомимся с функцией выделения памяти и рассмотрим подробный пример работы этой фишки.

Иногда при проектировании больших скриптов возникают проблемы с хранением каких-либо значений в CLEO-скриптах:

  • Количество локальных переменных мало;

  • Глобальные переменные не рекомендовано использовать в CLEO;

  • Глобальные CLEO-переменные могут привести к конфликтам между скриптами;

Проблемы очевидны и решить их можно несколькими способами. Сегодня мы рассмотрим способ с выделением динамической памяти.

Допустим, у нас стоит задача: создать 100 актёров. Их нужно где-то хранить, и буфер является очень привлекательным вариантом. Рассмотрим опкоды, для работы с ними:

0AC8: 0@ = allocate_memory_size 120
0AC9: free_allocated_memory 0@

Первый опкод выделяет память под буфер, где 120 — это размер (в байтах), а 0@ — переменная, которая будет хранить указатель на начало области памяти (он же совпадает с началом буфера). Второй опкод освобождает выделенную память.

Возникает вопрос: как узнать размер выделяемой памяти, чтобы нам хватило места для записи этих актёров? Поскольку переменные для создания/записи актёров являются ссылками, то размер одного сегмента этой памяти будут равняться 4 байтам, а общий размер выделяемой памяти — 100 × 4 = 400 байт.

Для того, чтобы получить 1 элемент этого буфера, легче всего сделать несколько SCM-функций, которые делали бы навигацию по этой области памяти. Первый участок кода будет выделять память под актёров, а второй — очистка выделенной памяти:

goto @SKIP_FUNCTIONS

:CREATE_ARRAY
0A90: 2@ = 4 * 0@ // 4 * size
0AC8: 1@ = allocate_memory_size 2@
0AB2: ret 1 1@

:DESTROY_ARRAY
0AC9: free_allocated_memory 0@
0AB2: ret 0

:SKIP_FUNCTIONS

Я назвал SCM-функции "создать массив" и "удалить массив" согласно принципам, по которым они работают. Теперь добавим возможность читать/записывать в ячейку памяти нужное значение по индексу. Индекс будет определятся как Номер × размер, размер ссылки естественно 4 байта:

Поскольку у нас идёт работа с памятью, мы обязательно применяем опкоды 0A8C и 0A8D для чтения и записи её участка. Адрес чтения мы получили за формулой: Начало буфера + ( размер ссылки × индекс ). Поэтому наши SCM-функции будут обязательно принимать ссылку на выделенный буфер:

Итак, запишем в наш массив 100 случайных актёра, разместив их у Гроув Стрит:

Результат:

100 актёров на Гроу

Как видим, в скрипте использовалась всего 1 переменная 1@ и мы не потеряли полный доступ ко всем остальным актёрам. Давайте теперь удалим созданных актёров и посмотрим работают ли наши функции удаления и очищения памяти:

Результат:

Актёров уже нет Гроу

Улица снова пуста! Интересно понаблюдать за самим процессом создания/удаления этих актёров: видно сколько примерно времени занимает одна итерация цикла.

Думаю, многие заметили, что в цикл я добавил нетипичную для него задержку в 0 милисекунд. В нашем случае без задержки игра вылетит, что конечно не радует. Надеюсь, что этот пример пригодится вашим проектам, где есть смысл хранить данные именно таким способом.

triangle-exclamation
triangle-exclamation
circle-exclamation

Кроме актёров, таким вот образом можно записать и другие сущности, а также целые и/или вещественные числа. Также нет привязки к конкретному типу данных: мы можем в разные ячейки записать разные значения. Главное не забыть что и куда было записано :)

На этом, пожалуй, всё. С Вами был wmysterio!

Last updated