Введение в программирование на SourcePawn. Часть 3.

Сообщения
207
Реакции
420
Помог
10 раз(а)
Введение в программирование на SourcePawn. Типы переменных, структура кода.

SourcePawn знает 6 основных типов переменных (в списке их, на самом деле, 7, но последний - "волшебный"):
  • int - целочисленное число, занимает в памяти 4 байта. Допустимое число для хранения в этой переменной - от -2147483648 до 2147483647. Эти числа, кстати, можно получить в коде. Они представлены константами cellmin и cellmax, соответственно.
  • char - символ строки, занимает в памяти 1 байт. SourcePawn полностью поддерживает Unicode.
  • float - числа с плавающей запятой (дробные), занимают в памяти 8 байт.
  • Handle - указатель на некоторые переменные в памяти самого SourceMod, занимает 4 байта. Этим типом может быть представлено что угодно, начиная с соединения к БД, заканчивая самим плагином. Мы к этому типу ещё вернёмся в отдельной статье.
  • Address - адрес в памяти самого сервера, занимает 4 байта.
  • Function - указатель на функцию, занимает 4 байта.
  • any - волшебный тип. Точный размер, занимаемый этим типом переменной, варьируется от 4 до 8 байт (в зависимости от того, что в неё в итоге записать).
Есть ещё множество других видов "переменных" (Action, CSWeaponID, TFTeam и др.), которые по сути являются тем же числом, но в другом виде, более удобном для нашего глаза в определённой сфере применения.

В отличие от Си и любого нормального языка, где мы имеем функцию, которая является "входной точкой", SourcePawn её не имеет. Виртуальная машина вызывает его публичные функции, когда что-то происходит. Например, при старте плагина, вызывается OnPluginStart(), а при выгрузке - OnPluginEnd(). Мы ещё вернёмся к этому.

Теперь немного о структуре языка. Он "блочный".
Код:
void SomeFunction()
{
  // Блок 1
  DoSomeAction();

  if (DoSomeResult() == true)
  {
    // Блок 2
    PrintToServer("Yeah! DoSomeResult() returned true!");

    {
      // Блок 3
      // SourcePawn может делать вложения блоков без каких-либо условий и циклов. Этим можно пользоваться, к слову. Все переменные, созданные в блоке, при выходе из него уничтожаются.
      int iMyData = 1337;
      PrintToServer("MyData equals to %d", iMyData);

      // Конец блока 3
    }

    // Конец блока 2
  }

  // Конец блока 1 
}
Что здесь?
  • Объявлена функция SomeFunction, которая ничего на вход не принимает, является скрытой (вызвать извне (из другого плагина или расширения), не передав указатель - нельзя), и ничего не возвращает (void). При входе в неё начинается выполнение блока "Блок 1"
  • Вызывается функция DoSomeAction(), на которую ничего не передаётся (содержимое скобок - пусто), и ничего из неё не принимается (нет никакого приравнивания к некой переменной).
  • В блоке условия (if) вызывается функция DoSomeResult(), на которую, снова, ничего не принимается, но результат которой записывается во временную переменную (которую не видно), и которая в конечном счёте сравнивается с true.
  • Если значение совпало - виртуальная машина начинает выполнять содержимое блока "Блок 2". Если нет - пропускает его.
    • В блоке 2 вызывается PrintToServer() функция, на которую передаётся некий текст.
    • Далее виртуальная машина переходит к выполнению блока 3, т.к. никаких "условий" перед ним и циклов нет.
      • В блоке 3 создаётся переменная типа int с именем iMyData. Ей сразу присваивается значение 1337.
      • Вызывается PrintToServer() функция, на которую передаётся уже два аргумента: текст и переменная. Переменная копируется, потому любые изменения оной "на той стороне" не повлияют на неё в этом блоке.

Мы можем объявлять блоки где угодно. Но всё же, компилятор нас обязывает объявлять "блок" обязательно при объявлении функции (в старом синтаксисе, к слову, мы этого могли и не делать). Мы ещё вернёмся к этому при изучении "условий", "циклов", т.к. блоки в основном фигурируют именно там.
Для создания переменной, обычно делается запись вида:
Код:
тип_переменной название_переменной;
У переменной может быть стандартное значение:
Код:
тип_переменной название_переменной = значение;
Если Вы пытаетесь присвоить переменной типа int, например, значение другой переменной иного типа (например, float), компилятор нас отругает "предупреждением" tag mismatch. Но если Вы уверены в легитимности своих действий, можно принудить компилятор считать, что эта переменная - другого типа:
Код:
int g_iPi = view_as<int>(3.14);
Последствия, однако, могут быть неожиданными. float, например, так мирно в int не преобразуется:
РИСУНОК 2.png
Ну, и отмечу, что за попытку преобразовать переменные некоторого типа, компилятор вообще ругает. За попытку преобразовать char или Function во что-то иное, Вы гарантированно получите "ошибку".

Глобальные, локальные переменные. Константы.
Теперь возможно имеет смысл поднять тему, что же это такое, переменные.
Если проводить аналогию, то каждый из Вас рано или поздно сталкивался в школе на математике с подобием переменных - x (икс), y (игрек), и так далее. Обычно в уравнениях надо найти x, зная какие-то другие переменные. Как и в математике, переменная в себе что-то хранит: число, указатель на участок памяти, символ.

Вот только в отличие от математики, в программировании выделяют два типа переменных: глобальные и локальные.
Глобальные доступны во всём коде. Мы можем редактировать их значения из одной функции, читать из второй, передавать какому-то другому плагину из третьей, и так далее... Они нам доступны всегда без каких-либо лишних телодвижений. Память для них выделяется виртуальной машиной при старте плагина. Объявляются они вне функций.
Локальные же наоборот. Память для них выделяется на время выполнения функции, и после выполнения, очищается. А объявлены прямо в теле функции.
Чтобы проще было понять, приведу фрагмент кода:
Код:
int g_iData; // глобальная переменная

public void OnPluginStart() {
  g_iData++;
  DoLogic();

  g_iData++;
  DoLogic();

  // вызовет ошибку ещё на стадии компиляции:
  // PrintToServer("%d", iData);
}

void DoLogic() {
  int iData; // локальная переменная
  iData++;

  PrintToServer("%d %d", g_iData, iData);
}
При запуске этого плагина, в консоли последовательно высветится:
Код:
1 1
2 1
При каждом запуске функции, память для переменной iData снова и снова выделяется. По-умолчанию, значение переменной - это 0. После выполняется увеличение на единицу, и вывод глобальной переменной, и локальной в консоль сервера.
 
Последнее редактирование:
Сообщения
2,713
Реакции
2,995
Помог
59 раз(а)
константами cellmin и cellmax, соответственно.
__cellmin__ - не так ли указаны?
20 Мар 2018
any - волшебный тип.
в AMXX Pawn и наверняка в старом синтаксисе SourcePawn так же используется _: Var;
20 Мар 2018
Он полностью "событийный" язык:
Event-based
 
Сообщения
207
Реакции
420
Помог
10 раз(а)
Сообщения
2,713
Реакции
2,995
Помог
59 раз(а)

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу