[SourcePawn] Урок 1 - Основы языка (Часть 1)

Zanoza

Команда форума
Арбитр
Сообщения
472
Реакции
81
Баллы
28
Intro:
Учить программирование на павне не стоит. Лучше начать с c++ например (не весь конечно, только основы).
Это даст базовые знания, понятия и терминологию для изучения других языков, ведь зная один язык разобраться в другом значительно проще.

Комментарии

Комментарий
– это текст, который предназначен для программистов и не обрабатывается компилятором. Обычно комментарии используются для создания заметок к коду для дальнейшего использования. Комментарии можно использовать при тестировании, чтобы сделать неактивными определенные строки кода.

Комментарии записываются одним из следующих способов:

Однострочный
Комментирует текст после // до конца строки.
Код:
текст1 // текст2текст3
текст2 будет комментарием

Многострочный
Комментирует весь текст между /* и */.
Код:
текст1
/*
текст2
*/
текст3
текст2 будет комментарием.
Многострочный комментарий может быть в одной строке:
Код:
текст1 /* текст2 */ текст3
Нельзя использовать многострочный комментарий внутри другого многострочного, на строку содержащую открывающий тег будет выдана ошибка, а закрывающий тег закроет первый комментарий

Переменные

Переменная
- это именованный участок оперативной памяти, используемый для временного хранения данных.
Переменная это своего рода емкость для хранения данных. Когда информация записана в переменной, тогда эту информацию можно получать и изменять.
Типы данных
Каждая переменная имеет свой тип.
Тип определяет сколько байт в памяти занимает переменная, какие значение она может принимать и как с ней вообще работать.
SourcePawn имеет такие базовые типы:
  • int - Целые числа. (Занимает 4 байта (32 бита), имеет пределы: от −2 147 483 648 до 2 147 483 647)
  • bool - Логический. (Занимает 4 байта (32 бита), может принимать только 2 значения: true (истина) и false (ложь))
  • float - Числа с плавающей точкой. (Занимает 4 байта (32 бита), имеет пределы: от −2 147 483 648.0 до 2 147 483 647.0)
  • char - Символьный тип. (Занимает 1 байт (8 бит) (но хранится в 4-х байтных ячейках), имеет пределы: от 0 до 255 (хранит код символа))
  • Handle - Дескриптор, указатель на объект. (Занимает 4 байта (32 бита))
    Является родительским для ряда других типов, о которых будет говориться в других уроках.
    Большинство объектов данного типа и производных от него после использования необходимо удалять с помощью оператора delete:
Код:
delete имя_переменной;
// Например
delete hMenu;
Этот оператор освобождает память выделенную под объект но не очищает саму переменную, поэтому для безопасности после delete, переменной лучше присвоить значение null.
Так же такие объекты можно клонировать (но не все).
Более подробную информацию о типах и их свойствах можно найти здесь: Handles (SourceMod Scripting) - AlliedModders Wiki
  • Function - Указатель на функцию. (Занимает 4 байта (32 бита))
Создание переменных
Есть 2 понятия при создании переменных:
  • Объявление переменной - указание типа переменной и её имени.
  • Инициализация переменной - присвоение начального значения. Инициализация может производится как при объявлении переменной так и после этого.
При создании переменной сначала указывается её тип, затем имя, после этого если нужно ставится символ присвоения и затем значение.
Код:
int a;        // Объявление переменной типа int с именем a
float b;    // Объявление переменной типа float с именем b

int c = 5;        // Инициализация переменной начальным значением 5
bool d = false;    // Инициализация переменной начальным значением false

int e;
e = 8;    // Инициализация переменной значением 8 после объявления

// Создаем сразу 3 переменные
int f = 7,    // Инициализация переменной f значением 7
    g = 14,    // Инициализация переменной g значением 14
    h;          // Объявление переменной h (начальное значение будет 0)
В SourcePawn если начальное значение не указано переменные инициализируются значением 0 для своего типа.
Это значит что для int это 0, для float - 0.0, для bool - false, для Function - INVALID_FUNCTION, Handle и его дочерних типов - INVALID_HANDLE или null
Преобразование типов

В SourcePawn можно выполнять явное преобразование типов с помощью оператора view_as.
Синтаксис: view_as<тип>(значение)
Код:
int a = 5;
float b = 7.0;

float c = b + view_as<float>(a);    // Значение переменной a преобразуется в тип float

int d = a + view_as<int>(3.5);    // В этом случае число 3.5 будет приведено к типу int, при этом будет отброшена дробная часть.
//    В результате d будет равно 8
Виды переменных
Переменные бывают:
  • Локальные - созданные в блоке кода
  • Глобальные - созданные вне блоков кода
Тут следует ввести понятие "Область видимости переменной" - область в которой переменная видимая и доступна.
Если переменная глобальная - к ней можно обратиться в любой части плагина, её область видимости - весь плагин.
Если переменная локальная - она видна только в пределах блока в котором она создана.
Пример:
Код:
int g_iGlobalVar;    // Глобальная переменная

public void OnMapStart()
{
    int iLocalVar = 5;    // Локальная переменная
    
    if(iLocalVar == 5)
    {
        int iLocalVar2 = iLocalVar+2;    // Локальная переменная
    }
}
Область видимости:
upload_2016-9-4_18-10-48.png
Так же переменные бывают:
  • Статические Локальные - область их видимости как у локальных переменных но создаются они только один раз и не удаляются после завершения функции, а сохраняют свое значение для следующего вызова.
Код:
MyFunc()
{
    static float fDamage;
// При первом вызове функции MyFunc будет объявлена переменная fDamage и буде равна 0.0
// При последующих вызовах функции MyFunc переменная fDamage уже не будет создаваться в памяти заново, а сохранит свое значение.
    fDamage = fDamage + 5.2; // 0.0 + 5.2 = 5.2;
// При первом вызове функции, значение fDamage равно 0.0, в строке выше  к ней прибавляется значение 5.2
// Если функция будет вызвана 2-й раз то fDamage уже будет равна 5.2 и после прибавления станет равна 10.4
// И так будет продолжаться с каждым вызовом функции
}
  • Такие переменные применяются в функциях, которые вызываются очень часто (особенно для массивов) и поэтому есть затраты времени на создание переменных при каждом вызове.
  • Статические Глобальные - аналогично глобальной переменной но такая переменная будет видна только в пределах того файла, где она объявлена.
Ограничение на имена переменных
Имя переменной может содержать от одного до 32 символов. Разрешается использовать строчные и прописные буквы, цифры и символ подчёркивания. Первым символом обязательно должна быть буква или символ нижнего подчеркивания _. Имя переменной не может совпадать с зарезервированными словами.
Рекомендации по названиям переменных
  • Первым символом имени ставить сокращение от типа переменных (int - i, float - f, bool - b, char - sz или s, Handle - h)
Код:
int iVar = 5;
float fVar = 3.3;
bool bVar = true;
char szVar[] = "строка";
Handle hPlugin = null;
Название глобальных переменных лучше начинать с приставки g_
Код:
int g_iVar = 5;
float g_fVar = 3.3;
bool g_bVar = true;
char g_szVar[] = "строка";
Handle g_hPlugin = null;
  • Имена констант писать в верхнем регистре
  • Название переменной должно кратко отобрать её назначение, если переменная состоит из нескольких слов то разделять их нижним подчеркиваний _ либо писать слитно но первую букву каждого слова писать в верхнем регистре
Код:
int iClient;
float fDamage = 3.3;
bool bVar = true;
char szPlayerName[] = "никнейм";
Handle hPluginIterator = null;
 

Zanoza

Команда форума
Арбитр
Сообщения
472
Реакции
81
Баллы
28
Массивы
Массив
– последовательный набор элементов одного типа именуемый одним именем.
Индекс – номер элемента массива.
Код:
int iArray[6];    // Создаем массив размером 6
Мы создали массив из 6-ти элементов. Нумерация элементов массива начинается с 0. Следовательно, мы получили 6 элементов номера которых 0 - 5.
Для обращения к элементу массива нужно указать имя массива и в квадратных скобках индекс элемента
Код:
iArray[0];    // 1-й элемент массива
iArray[1];    // 2-й элемент массива
...
iArray[4];    // 5-й элемент массива
iArray[5];    // 6-й элемент массива

iArray[3] = 5;    // Записываем число 5 в 4-й элемент
Массивы так же можно инициализировать при объявлении:
Код:
int iArray[4] = {4, 16, -2, 8};
Код:
int iArray[8] = {-1, ...};    // Таким образом, весь массив будет инициализирован значением -1
Если при объявлении указать все элементы массива, то размер массива можно не указывать. Компилятор это сделает сам.
Код:
int iArray[] = {8, 3, 9, 6};    // Компилятор установит размер массива - 4
Строки
Строка
– массив типа char.
В строке символы записываются последовательно и конец строки обозначается нулем. Не символом нуля ( '0' ), а именно нулем ( '\0' или 0 ).
При этом существуют понятия как размер строки и длина строки.

Размер строки - размер массива указанный при создании

Код:
char sString[32];    // Размер строки - 32
Получить размер строки строки можно функцией sizeof
Код:
char sString[32];
PrintToServer("Размер строки: %i", sizeof(sString));
Результат:
Код:
Размер строки: 32
Длина строки - количество символов записанных в строке. (количество символов до нулевого)
Код:
char sString[32] = "string";    // Размер строки - 32
//    Длина строки - 6
Получить длину строки строки можно функцией strlen
Код:
char sString[32] = "длина строки";
PrintToServer("Длина строки: %i", strlen(sString));
Результат:
Код:
Длина строки: 12
К отдельным символам в строке нужно обращаться как к элементам массива
Символ- одна ячейка в строке. Заключен в одинарные кавычки.
Код:
char sString[32] = "строка";
sString[0] = 'С'; // Заменяем первый символ на 'С'
Чтобы очистить строку достаточно установить первым элементом строки нулевой символ:
Код:
char sString[32] = "строка";
sString[0] = 0; // Либо sString[0] = '\0';
После этого строку можно считать пустой.
Для работы со строкам в SourcePawn существуют определенные функции (string · SourceMod Scripting API Reference)

Многомерные массивы
Код:
int iArray[3][5];    // Двухмерный массив 3 на 5
В созданном массиве iArray есть 3 элемента, на каждый из которых приходится еще 5. И того все элементы массива iArray:
Код:
iArray[0][0];
iArray[0][1];
iArray[0][2];
iArray[0][3];
iArray[0][4];

iArray[1][0];
iArray[1][1];
iArray[1][2];
iArray[1][3];
iArray[1][4];

iArray[2][0];
iArray[2][1];
iArray[2][2];
iArray[2][3];
iArray[2][4];
Например, массив 8*8 визуально выглядит как шахматная доска, 8*8*8 как куб по 8 элементов во все стороны.
Динамические массивы
SourcePawn при создании массива требует указания размера как константы. Поэтому нельзя создать массив размером, которого является значение переменной:
Код:
int a = 7;
int b[a];
Получаем ошибку:
Код:
error 161: brackets after variable name indicate a fixed-size array, but a dynamic size was given - did you mean to use 'new int[size]' syntax?
Эту проблему позволяют решить динамические массивы.
Код:
int a = 7;
int[] b = new int[a];
Абстрактный тип данных
В SourcePawn существуют объекты абстрактного типа данных, о них будет идти речь в следующих уроках.

Операторы
  • Присвоения ( = )
    Присваивает аргументу стоящему слева от оператора, значение стоящее справа:
Код:
int a = 7;    // Слева a, справа 7. Присваиваем a значение 7
Математические:
  • Сложение ( + )
  • Вычитание ( - )
  • Умножение ( * )
  • Деление ( / )
  • Остаток от деления ( % )
Объединение математических операторов и операторов присвоения
Код:
int a = 7;
int b = 4;
b += a;    // Идентично b = b + a;
Аналогично делается и с другими операторами

Приоритет операций
Приоритеты описаны здесь Приоритет операций C++ — cppreference.com (не всё актуально для SourcePawn)

Инкремент, декремент
Инкремент (++)
– операция увеличения значения на еденицу.
Декремент (--) – операция уменьшения значения на еденицу.
Обе эти операции бывают 2-х видов:
  • Префиксный - сначала выполняется Инкремент/Декремент, затем получается значение.
  • Постфиксный - сначала получается значение, затем выполняется Инкремент/Декремент.
Код:
// Постинкремент
int a = 7;
a++;    // a будет равно 8

// Преинкремент
int a = 7;
++a;    // a будет равно 8

// В этом случае между видами инкремента не будет заметно разницы
Код:
// Постинкремент
int a = 7;
if(a++ == 8)    // Ложь. Сначала берется значение a, которое равно 7, сравнивается, а потом только выполняется прибавление
{
    // код
}

// Престинкремент
int a = 7;
if(++a == 8)    // Истина. Сначала выполняется прибавление, затем берется значение a, которое уже будет равно 8 и сравнивается
{
    // код
}
 

Zanoza

Команда форума
Арбитр
Сообщения
472
Реакции
81
Баллы
28
Условные операторы
  • Условный оператор if
    Выполняет проверку истинности условия
Код:
if(условие)
{
    // код, который будет выполнен если условие истинно
}
Код:
int a = 5;
if(a > 2)    // Условие выполнится т.к. 5 > 2
{
    // код
}
Ключевое слово else
Всегда идет после кода условия if. Выполняется в случае если условие if не выполнилось
Код:
if(условие)
{
    // код если условие истинно
}
else // Иначе
{
    // код если условие не истинно
}
Код:
int a = 1;
if(a > 2)    // Условие не выполнится т.к. 1 не больше 2
{
    // код
}
else    // Код ниже будет выполнен т.к. не было выполнено условие if
{
    // код
}
Конструкция if ... else if ... else
Например
Код:
int a = 5;
if(a > 9)    // Если 5 > 9
{
    // код
}
else if(a > 6)    // иначе если 5 > 6
{
    // код
}
else if(a > 3)    // иначе если 5 > 3
{
    // код
}
else    // иначе
{
    // код
}
Сокращенный if
Синтакис:
Код:
(логическое выражение) ? (если выражение истинно) : (если выражение ложно)
Если логическое выражение истинно то выбирается значения стоящее слева от двоеточия, иначе - значение справа.
Применяется он для проверок внутри выражений.
Код:
int a = 5;
int b = 8;
int c = 10 - ((a > b) ? b:a);
Если a > b то выражение примет вид:
Код:
int c = 10 - b;
иначе:
Код:
int c = 10 - a;
На примере строк:
Код:
bool bValue = true;
char sBuffer[64];
FormatEx(sBuffer, sizeof(sBuffer), "Значение: %s", bValue ? "Вкл":"Выкл");
Если bValue будет равна true то строка примет вид:
Код:
"Значение: Вкл"
Если же false то
Код:
"Значение: Выкл"
Условный оператор switch
Код:
switch (выражение)
{
    case значение_1:
    {
        // код
    }
    case значение_2:
    {
        // код
    }
    case значение_3:
    {
        // код
    }
    case значение_n:
    {
        // код
    }
    default:
    {
        // код
    }
}
Оператор работает следующем образом. Вычисляется значение выражения. Затем выполняются операторы, помеченные значением, совпадающим со значением выражения. То есть если, выражение принимает значение_1, то выполняются операторы_1 и т.д.. Если выражение не принимает ни одного из значений, то выполняются операторы, расположенные после слова default.
Ветвь default может отсутствовать.
Код:
int a = 6
switch (выражение)
{
    case 0:
    {
        // a равно 6
        // код
    }
    case 1,2:
    {
        // a равно 1 или 2
        // код
    }
    case 3,4:
    {
        // a равно 3 или 4
        // код
    }
    case 5,6,7:
    {
        // a равно 5 или 6 или 7
        // код
    }
    default:
    {
        // a не равно ни одному значению выше
        // код
    }
}
Выражением внутри switch() может любое целочисленное выражение, или выражение у которого есть однозначное преобразование к этому типу.
Операторы отношения
  • Равно ( == )
    Выполняет проверку, равны ли значения слева и справа от оператора
Код:
if(a == b) // Если a равно b
{
    // код
}
Не равно ( != )
Выполняет проверку, не равны ли значения слева и справа от оператора
Код:
if(a != b) // Если a не равно b
{
    // код
}
Для проверки != 0 существует сокращение
Код:
// Для int
if(iValue) // Аналогично if(iValue != 0)
{
    // код
}
    
// Для bool
if(bValue) // // Аналогично if(bValue != false) или  if(bValue == true)
{
    // код
}
    
// Для float
if(fValue) // // Аналогично if(fValue != 0.0)
{
    // код
}
    
// Для Handle и его дочерних типов
if(hValue) // // Аналогично if(hValue != null)
{
    // код
}
Или его обратная форма:
Код:
// Для int
if(!iValue) // Аналогично if(iValue == 0)
{
    // код
}
    
// Для bool
if(!bValue) // // Аналогично if(bValue != true) или  if(bValue == false)
{
    // код
}
    
// Для float
if(!fValue) // // Аналогично if(fValue == 0.0)
{
    // код
}
    
// Для Handle и его дочерних типов
if(!hValue) // // Аналогично if(hValue == null)
{
    // код
}
Больше ( > )
Выполняет проверку, больше ли значение слева от оператора, чем значение справа
Код:
if(a > b) // Если a больше b
{
    // код
}
Больше или равно ( >= )
Выполняет проверку, больше или равны ли значение слева от оператора, чем значение справа
Код:
if(a >= b) // Если a больше или равно b
{
    // код
}
Меньше ( < )
Выполняет проверку, меньше ли значение слева от оператора, чем значение справа
Код:
if(a < b) // Если a меньше b
{
    // код
}
Меньше или равно ( <= )
Выполняет проверку, меньше или равны ли значение слева от оператора, чем значение справа
Код:
if(a <= b) // Если a меньше или равно b
{
    // код
}
Логические операторы
  • И (&&)
    Возвращает истину, только если оба выражения тоже истинны
Код:
if(a > b && b < c) // Если a больше b и b меньше c
{
    // код
}
ИЛИ (||)
Возвращает истину, только если хоть одно из выражений тоже истинно
Код:
if(a > b || b < c) // Если a больше b или b меньше c
{
    // код
}
НЕ (!)
  1. Сначала приводит аргумент к логическому типу true/false.
  2. Затем возвращает противоположное значение.
Код:
if(!(a > b && b < c))
{
  // код
}
Для соблюдения логики действий и проверок выражения лучше брать в скобки:


Битовые операторы

Выполняют операцию с битами в числах
  • И (&)
  • ИЛИ (|)
  • НЕ (~)
  • Исключающее ИЛИ (^)
  • Сдвиг влево (<<)
  • Сдвиг вправо (>>)
Подробнее о них будет рассказано в следующих уроках.
 
Верх