Добавлен nazarpunk,
опубликован
JASS
Содержание:
Переменные - это выделенные ячейки в памяти под определенный тип данных.
Глобальные переменные
Переменные, доступные из любого места в коде.
globals
Блок, для объявления глобальных переменных в коде. Как и в случае с функциями из common.ai, допустимо любое количество блоков до объявления первой пользовательской функции.
globals
integer a = 1
real b = 2
boolean c
endglobals
В него, при генерации war3map.j попадают переменные созданные в редакторе триггеров. Эти переменные будут иметь префикс udg_ (user defined global).
Выполняется до инициализации карты и соответственно в нём нельзя создавать никаких хэндлов.
constant
Ключевое слово, которое должно намекать разработчику что значение этой переменной не будет изменяться за всё время исполнения кода.
globals
constant integer a = 1
constant real b = 2
constant boolean c = false
endglobals
Так же, при попытке сменить значение constant переменной произойдёт ошибка. Но это не точно.
Локальные переменные
Переменные, которые живут и умирают внутри функции. Должны быть объявлены до любого действия внутри функции с помощью ключевого слова local:
function main takes nothing returns nothing
local integer a = 1
local real b
endfunction
Утечка
Она же уточка, она же memory leak, это не освобождённая память. Возникает по двум причинам:
- не удалённые объекты
- локальные переменные с типом, наследуемым от handle
Первая причина проста - любой созданный объект необходимо удалить после себя. Да, прям как в сортире - создали, полюбовались, удалили.
function SomeTriggerAction takes nothing returns nothing
local location rally = GetUnitRallyPoint(GetTriggerUnit())
// делаем что-то с полученной точкой
call RemoveLocation(rally) // удаляем созданную точку после использования
set rally = null // об этом ниже
endfunction
Вторая причина не интуитивна и заключается в рукожопости создателей языка. Более подробно это описано здесь.
Чтоб их избежать достаточно запомнить простое правило: в момент выхода из функции всем локальным переменным с типом наследующим handle должно быть установлено значение null.
Напоминаю, что все типы объявленные в common.j наследуют handle, а выход из функции происходит когда исполняется endfunction или return.
Уточню для самых одарённых - это ошибка языка и относится только к локальным переменным. Ни глобальные перемененные, ни аргументы функции этой ошибке не подвержены.
set
Для установки значения переменным используется ключевое слово set. Установка значения в выражении недопустима.
globals
integer a
endglobals
function main takes nothing returns nothing
local integer b
set a = 1
set b = 2
endfunction
Инициализация
Несмотря на умное слово, это означает всего лишь первую установку значения переменной, у которой существует особое состояние - объявленая, но не инициализоранная переменная. При попытке получить значение такой переменной игра завершит исполнение текущей функции.
globals
integer a // переменная объявлена, но не инициализирована
integer b = 1 // переменная объявлена и ей установлено значение, тоесть она проинициализирована
endglobals
function main takes nothing returns nothing
local integer c // переменная объявлена, но не инициализирована
local integer d = 1 // переменная объявлена и ей установлено значение, тоесть она проинициализирована
set c = 2 // первая установка значения переменной, тобишь инициализация
set d = 3 // значение установлено при объявлении и это просто установка значения
endfunction
Немного лирики
Переменной с любым типом необязательно присваивать значение при объявлении. Но считается хорошим тоном всегда инициализировать переменные при объявлении. Делайте так и вас почешут за ушком.
globals
integer i = 0
real r = .0
boolean b = false
string s = "" // если вы используете null для строк, то вы больной ублюдок и вам пора лечиться
unit u = null // для любых потомков handle используйте null, который собственно для них и создавался
endglobals
Массивы
Для объявления массива используется ключевое слово array. Инициализировать массив, как и указать его размер при объявлении нельзя. Что чертовски неудобно.
Индексация значений начинается с нуля.
globals
integer array a
endglobals
function main takes nothing returns nothing
local integer array b
set a[0] = 1
set b[0] = a[0] + 2
endfunction
Для типа code массивы не поддерживаются.
Выделение памяти
Изначально любой массив имеет размер ноль. При записи значения выделяется память на количество элементов, равное значению ближайшей сверху степени двойки. Так продолжается, пока размер не достигнет максимального, указанного в переменной JASS_MAX_ARRAY_SIZE из common.j.
function main takes nothing returns nothing
local integer array a // размер равен нулю
set a[0] = 1 // ближайшая степень к 0 это 2^0=1, размер равен 1
set a[1] = 1 // ближайшая степень к 1 это 2^1=2, размер равен 2
set a[2] = 1 // ближайшая степень к 2 это 2^2=4, размер равен 4
set a[3] = 1 // ближайшая степень к 3 это 2^2=4, размер равен 4
set a[4] = 1 // ближайшая степень к 4 это 2^3=8, размер равен 8
set a[5] = 1 // ближайшая степень к 5 это 2^3=8, размер равен 8
set a[6] = 1 // ближайшая степень к 6 это 2^3=8, размер равен 8
//...
endfunction
Ниже приведена таблица степеней двойки и максимальный размер массива для разных версий игры.
Число | Степень | Значение | Максимальный размер |
2 | 0 | 1 | |
2 | 1 | 2 | |
2 | 2 | 4 | |
2 | 3 | 8 | |
2 | 4 | 16 | |
2 | 5 | 32 | |
2 | 6 | 64 | |
2 | 7 | 128 | |
2 | 8 | 256 | |
2 | 9 | 512 | |
2 | 10 | 1 024 | |
2 | 11 | 2 048 | |
2 | 12 | 4 096 | |
2 | 13 | 8 192 | Ванила |
2 | 14 | 16 384 | |
2 | 15 | 32 768 | Reforged |
2 | 16 | 65 536 | |
2 | 17 | 131 072 | |
2 | 18 | 262 144 | UjAPI |
Маленькая хитрость
Массив должен располагаться в памяти непрерывно, такова особенность архитектуры. Поэтому при смене размера будет найден участок памяти в который поместится новый массив и все значения будут скопированы туда. Как вы понимаете, эта операция не бесплатна и чтоб избежать лишних операций с памятью, можно прикинуть максимальное количество элементов массива и сразу выделить необходимую память:
globals
integer array a
endglobals
function main takes nothing returns nothing
set a[500] = 0 // Допустим мы прикинули, что количество элементов будет около 300
endfunction
Память выделяется в момент присваивания значения по индексу, так что само значение неважно:
globals
integer array i
string array s
unit array u
rect array r
endglobals
function main takes nothing returns nothing
set i[500] = 0
set s[500] = ""
set u[500] = null
set r[500] = null
endfunction
Утечка
Здесь всё практически так же как с переменными: глобальные массивы ведут себя как глобальные переменные и в первом приближении можно вообще относиться к ним как к переменным с хитрым синтаксисом.
Локальные массивы с типом не наследующим handle абсолютно безопасны и их использование дело вкуса.
О локальных массивах с типом наследующим handle лучше вообще забыть.
Локальные массивы с типом наследующим handle просто шикарно отстреливают жопы. При изменении размера будет утечка четырёх байт на каждое значение отличное от null. Поэтому таким массивам обязательно выделять память, гарантировано покрывающим все элементы. А так же при выходе их функции в таком массиве не должно быть значений отличных от null.
Коллизии имён
Как писалось выше, язык написан ногами, поэтому избегайте пересечения имён в одной области видимости. Ибо выстреливает такое всегда внезапно и довольно таки долго ищется.
globals
integer A = 1
endglobals
function B takes integer D returns nothig
local integer E = 2
//...
endfunction
function C takes integer D returns nothig
local integer E = 3
//...
endfunction
Как видите, в глобальной области видимости разные имена: A, B и C. Аргумент D и локальная переменная E в каждой функции видна только внутри этой самой функции и не пересекается ни с чем из глобальной области. Здесь должна быть ссылка на статью, где это подробно объяснено, но я такой не нашёл.
В программировании это называется shadowing, но вспоминаем предыдущие пассажи про ноги...
Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Отредактирован IceFog
Если перед выходом из функции обнулять все элементы, содержащие agent'ы, то ссылки на таблицу хэндлов тоже не утекут. Тут всё как и с обычными переменными.
Как то я случайно сломал свою базу данных, и функция обращалась к неиспользуемой ячейке массива. Массив boolexpr. Неинициализированный, то есть не стоит никакого цикла, который null или 0 расставляет. Ничего не вылетало, не глючило, но фильтр игра воспринимала как null и забирала в группу всех юнитов подряд.
Второй момент был связан с попыткой вывести имя юнита по айди, тоже функция залезла не в ту ячейку и выдала (Default string) вместо имени. То есть, в той ячейке стоял 0. Версия игры 1.26 соответственно.
Unryze
nazarpunk
Значит, массивы инициализировать не обязательно, если есть стандартное значение. Спорил я для того, чтобы установить истину ) а то один одно говорит, другой другое, это непорядок, нужно было узнать.
Отредактирован Unryze
Отредактирован EugeAl
Это действие инициал все предыдущие ячейки тоже?
Не желаю более возвращаться к этой теме.
Отредактирован makkad
Отредактирован nazarpunk
Отредактирован IceFog
Отредактирован ScorpioT1000
Редкий кейс, но раз уж покрывать, так целиком)