Урок 2: Видове променливи, Масиви, Стрингове, Функции в Pawn
Съдържание:
1. Променливите
2. Видовете променливи
2.1. Integer
2.2. Float
2.3. Booleans (булевите променливи, указващи дали дадено твърдение е вярно (TRUE - 1) или лъжа (FALSE - 0))
4. Масивите
5. Стринговете
6. Функции
Променливи
За да съхраним временно някаква по вид информация, ние използваме променливите.
Променливите могат да съдържат числа, символи и стрингове.
За да декларираме променлива е нужно да напишем
Код: |
1
| new IME_NA_PROMENLIVATA |
Важно е да се отбележе, че Small има 3 вида типове за деклариране на променливите.
По подразбиране е обикновено цяло число или още казано интежер.
Името на променливата трябва да започва с буква. Може да съдържа всички
букви в двата регистъра (горен и долен): A-Z , a-z , цифри в обхвата
0-9 и долна черта "_".
Много важно е да знаете че имената на променливите са чувствителни към регистъра.
Тоест, ако имате декларирана променлива "Test" и отделно още една
на име "test" - те са независими една от друга и съответно могат да
приемат едновременно различни стойности без да си пречат по някакъв
начин.
Както казах по-горе, променливите се делят на няколко вида според съдържанието си.
ИНТЕЖЕР
За да дефинираме някаква променлива, със съдържание интежер число (целите числа) трябва да напишете:
Ето и няколко други примера:
Код: |
1 2 3 4
| new a // Декларираме празна променлива
new b=5 // Декларираме променливата b и й зачисляваме стойност 5
new c=5.0 // Тук обаче имаме грешка ! Не можем да слагаме за стойност числа с плаваща точка (За целта идва Float променвлите)
new d="test" // "test" не е никакво число, следователно тази променлива се явява невалидна |
За да спестявате редове, можете да декларирате и променливите си на един ред, изброявайки ги просто с по една запетайка ",".
Също така за по пригледност, можете на по-големите числа да им
отделяте десетиците, стотиците, хилядните и т.н. с долна черта "_". Но
и никакъв не е проблема да си ги напишете нацяло - Pawn няма да се
обърка.
Примерно искате да декларирате променлива chislo със стойност 100000, и трябва да напишете:
Код: |
1
| new chislo = 100000 |
Но можете да го запишете и по този начин:
Код: |
1
| new chislo = 100_000 |
.. същото е.
Ето и един простичък пример, как да декларирате няколко променливи на един и същ ред:
Код: |
1 2
| new e,f,g,h
new x=7, y=3 |
ПЛАВАЩИ (Float)
Също така както казах, можете да декларирате променливите си и като
"Float", което ще значи че могат да съдържат в себе си числа с
десетична запетая.
Или с други думи - числа с плаваща точка/запетая.
Код: |
1 2 3 4
| new Float:a // Декларираме float променлива без никаква стойност
new Float:b=5.3 // Декларираме променливата "b" и й присвояваме стойност = 5.3
new Float:c=5 // Това е също правилно, но компилатора ще ви даде warning при компилиране
new Float:d="test" // Това е неправилно, тъй като "test" не е никакво число, камоли пък float |
Също така можете да направите и следното:
Код: |
1 2 3 4 5 6
| //float(ЧИСЛО-n) е функция, която взема число "n" и го прави плаващо (за да не ви дава warning компилатора при компилиране)
new Float:promenliva = float(5)
new Float:promenliva2 = 5.0
new Float:promenliva3 = 1.0*5
new promenliva4 = floatround(5.0)
//Бележка: floatround(n) е функция, която взема числото "n" и го закръгля |
Отбележете също така че разстоянието между = не е от значение.
БУЛЕВИ
Това е последния тип променлива.
Много е простичка, тя винаги е или "истинна" или "лъжлива (булева)" или тъй наречените TRUE / FALSE.
Код: |
1 2
| new bool:Ubit_li_e_zalojnika // Декларира променлива Ubit_li_e_zalojnika автоматично със стойност FALSE (лъжа)
new bool:Ubit_li_e_zalojnika=true // Декларира Ubit_li_e_zalojnika със стойност TRUE |
Няма да се спирам много на този вид променлива, защото няма да ви трябва за сега и в процеса на работа ще я видите и в действие.
Но на кратко, може да се ползва за return-ване (връщане) на някакъв по вид резултат от някаква проверка (примерно).
Не искам да давам пример с проверки, защото още не сме стигнали до тях и не искам да ви обърквам.
За сега така ще оставим нещата, а пък ако сте заинтерисовани много, пишете и ще ви дам няколко примерчета.
Масиви
Масивите служат както променливите за да съхраняват някаква определена информация.
Предимство на масивите е че можете да записвате много стойности и то само в една променлива.
Масивите следват същите правила като обикновените променливи и се делят на същите типа.
Можете да дефинирате също така в големи скоби колко стойности да може да събере даден масив.
Код: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| // Декларираме променлива "Players", която ще побира в себе си 32 (в случая) числа.
new Players[32]
// Сега спокойно можем да запишем каквото си искаме в тези 32 слота, които сме направили в масива.
// Слотовете са номерирани и започват от 0 до n - 1. В тоя случай от 0 до 31 (което си е 32)
// Бележка: Всеки слот започва от 0
// Присвояваме стойност петица за слот 0 в масива
Players[0] = 5
// Така слагаме за слот 1, това, което сме записали за слот 0 (в случая е петица)
Players[1] = Players[0]
// Това тук е грешно !
// Въпреки че са 32 слота, те са номерирани както казах от 0 до 31.
// Ако направите същата грешка, компилатора ще върне грешка че сте излязли от обхвата на масива (index out of bounds)
Players[32] = 15
// Това тук също е грешно
Players[-1] = 6
new a = 3
// Както и това
// "a" трябва да е константо число
new GreshenMasiv[a]
// Горният начин, би трябва да изглежда така:
const b = 3
new PravilenMasiv[c]
// Също така можете да използвате и директиви (за повече информация, прочетете по-долу)
#define RAZMER_NA_MASIVA 3
new masiv[RAZMER_NA_MASIVA] |
Масивите също така могат да бъдат декларирани като група от данни:
Код: |
1 2
| new chisla[4] = {0,1,2,3}
// Забележете броя на цифрите и размера на масива ! |
Също така можете да използвате всеки един тип данни с масиви:
Код: |
1 2 3 4
| // Масив с плаващи числа
new Float:Numbers[4] = {0.0, 1.2, 2.4, 3.8}
// Масив с булеви данни.
new bool:playerHasGun[33] = {true, ...} |
Стрингове
При стринговете отпада декларирането на типа на променливата.
Стринговете технически са числа.
Стринга е вид масив от числа, които се превеждат/трансформират към ASCII символи.
Веднага давам и пример:
Код: |
1 2 3 4
| // Така ще декларираме масив moqString, който да съдържа информацията "test"
// Ще съдържа 5 слота, защото са 4ри символа
// Последният слот е запазен за числото 0, което всъщност казва на engine-а на pawn че това е стринг. И затова винаги трябва да имаме едно на ум!
new moqString[] = "test" |
Следният пример показва абсолютно същото нещо със същото значение, но е по-дълго и не се препоръчва.
Този начин сработва, тъй като всеки един символ от стринга "test" се е запазил в определен слот в масив.
Код: |
1 2 3 4 5 6
| new moqString[5]
moqString[0] = 't'
moqString[1] = 'e'
moqString[2] = 's'
moqString[3] = 't'
moqString[4] = 0 |
Напълно грешно е обаче това:
Код: |
1 2 3
| new moqString[6]
moqString = "Hello" // Не валидно !
moqString[0] = "Hello" // Не валидно ! |
За да запишете някаква информация в стринг, използвайте copy() функцията или format() , formatex:
Код: |
1 2 3 4 5 6 7 8
| new String[7]
copy(String, 6, "Hello")
new string2[7]
format(string2, 6, "Hello")
new string3[7]
formatex(string3, 6, "Hello") |
Разликите между функциите ще откриете в документацията: http://amxmodxbg.org/doc
.. сега обърнете внимание на това, че копирахме 6 клетки от масива в масив държащ 7.
Ако трябваше да копираме 7 байта в този масив, copy() функцията
щеше да копира допълнителен байт за празният (null) символ и да развали
масива.
Ето и още няколко примера:
Код: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // copy() е функция, която приема 3 параметъра
copy(дестинация[], дължина, източник[])
// Копира даден стринг, намиращ се в "източник[]" масива и го поставя в "дестинация[]" масива, но копира само до "дължина".
// И на последно място, за да ви докажа че стринга наистина е масив от числа, това е напълно валидно:
new test[7]
test[0] = 65
test[1] = 77
test[2] = 88
test[3] = 88
test[4] = 66
test[5] = 71
test[6] = 0
// Така ще присвоим стойност за променливата "test", стойността "AMXXBG". Невероятно, но факт. |
Функции
Pawn ви позволява да си дефинирате и декларирате свои собствени функции, без които нямаше да е толкова разнообразно всичко.
Функциите биха могли да ви помогнат с това че можете да ги
използвате колкото си искате пъти на различни места и ще ви спести
излишен код.
Особеното на функциите е че, те по принцип трябва да връщат накяква стойност.
За да върне някаква стойност, се използва return командата, която веднага след използването й, прекъсва изпълнението на функцията и връща съответната стойност.
Ето и един простичък пример:
Код: |
1 2 3 4 5 6
| amxmodxbg() {
log_amx("Tova e vtora chast ot uroka za AMX Mod X scriptiraneto !")
return 1
}
amxmodxbg() |
Нека и да разтълкуваме всичко по горепосоченият код.
amxmodxbg - е името на самата функция
() - в скобичките се слагат
параметрите, които функцията да присвоява и след това да си ги използва
в тялото (по-надолу ще видите и такива примери)
{ ..... } - Тялото на функцията се огражда от тези скобички. Обърнете внимание на първата че е: { , а крайната - затваряща е }
log_amx("Tova e vtora chast ot uroka za AMX Mod X scriptiraneto !") - log_amx е функция, която логва даден текст в сървърната конзола
return 1 - връщаме единица
Тук можете да видите и пример как функцията може да приема някакви параметри:
Код: |
1 2 3 4 5 6 7 8 9
| umnojenie(a, b) {
new rezultat = a * b
return rezultat
}
new chislo1 = 5
new chislo2 = 9
new rezultat = umnojenie(chislo1, chislo2) |
Както вече би трябвало да сте разбрали, името на функцията ни е "umnojenie" (и би трябвало да се досетите какво ще прави)
В скобичките, след името й са двата параметъра (a и b), която тя присвоява.
След това декларираме нова променлива rezultat, която ще извърши умножението на параметъра "a" и "b" и съответно ще го запази в себе си.
След това връщаме с return резултата, който сме получили.
.. до тук с функцията. Нека да разгледаме и как да я извикаме:
Декларираме променливите chislo1 и chislo2, като им присвояваме
някакви стойности (които, след това ще умножим) и накрая създаваме още
една променлива, която ще съдържа резултата, върнат от функцията ни umnojenie.
Ето и друг пример:
Код: |
1 2 3 4 5 6 7
| umnojenie(a, b) {
new rezultat = a * b
return rezultat
}
new rezultat = umnojenie(6, 5) |
.. където rezultat ще приеме стойност 40, a в горният пример 45
Когато давате параметри към дадена функция, се нарича "преминаване"
(passing). Можете да прекарате каквито и да е било по тип данни.
.. било то масиви, float и т.н.
Ето една примерна функция, която ще събере 2 float числа:
Код: |
1 2 3 4 5 6 7 8 9 10 11 12 13
| Float:add_two_floats(Float:first, Float:second)
{
new Float:sum = first + second
return sum
}
new Float:a
new Float:b
a = 5.0
b = 6.3
new Float:c
c = add_two_floats(a+b) |
И нека да я разтълкуваме:
1. Декларираме функция, която приема две float стойности.
2. Декларираме вътре във функцията променлива sum от типа float, която ще сумира двата параметъра
3. Връщаме като резултат от функцията стойността на променливата sum, в която вече е отговора
Както бях писал, можете да използвате и масиви като параметри.
Но тук не е нужно да определяте размера на масива, но все пак ако
го определите, трябва да ста сигурни че извиквате функцията с масив,
който е напълно еднакъв по размер и тип.
Пример:
Код: |
1 2 3 4 5 6 7 8
| add_two_from_array(array[], a, b)
{
new first = array[a]
new second = array[б]
new sum = umnojenie(first, second) // Използваме нашата функция "umnojenie", която декларирахме по-горе в урока
return sum
} |
Отбележете че, когато прекарвате масиви през функция, те преминават, както се казва "by referance".
.. хехе, не разбрахте нали ?
Сега ще се опитам да ви ги обесня малко нещата като вмъкна една отбивка към всичко казано до сега:
Има два начина за преминаване на аргументи/параметри към функция.
Първия начин се нарича "call by value" или на български "извикване
по стойност, но иначе е се вика по копиране" и другия метод е "call by
referance", или на български: "извикване чрез преписване".
Първият, който казах е "call by value" (чрез копиране): Знаете че при слагането на параметър във функция, той отива вътре във функцията и от там насетне се използва в нея.
Но всъщност това не е точно така. Тъй като по този метод
променливата не отива във функцията, а се копира и използва само
нейната стойност.
И тъй като се копира само стойността, а не променливата заедно с
нея, всяка промяна на "преминатата" променлива през функцията не се
запазва извън нея, тоест резултата няма да има ефект над по-нататъшните
действия извън тази функция за променливата, която сме използвали.
Сега ще ви го изесня с един прост пример:
Да предположим че имаме променлива със стойност = 6 преди да я извикаме във функцията.
Код: |
1 2 3 4 5 6 7 8 9
| subirane(a) {
new rezultat = a + a
a = a - 3
return rezultat
}
new a = 6
new rezultat = subirane(a) |
Резултата ще бъде равен на 10
Тъй като този метод копира и използва единствено стойността на променливата (в случая a) без самата нея, стойността на променливата ще продължава да бъде = 6, тоест няма да е = 10.
И ако желаете, пак можете да направите няколко пресмятания, но
предходните резултати, няма да влияят върху стойностите, които ще
влязат във функцията при извикването й.
Те отново ще са си = 6 и съответно, резултата винаги ще е 10, освен ако не използваме променливата "a" и в други функции.
"Call by Reference" (извикване чрез преписване)
В този случай самата променлива се използва за параметър/аргумент.
Иначе с прости думи казано, самият адрес до променливата преминава
за параметър и върнатата стойността от функцията се използва за
постоянна стойност, а не като при другият метод - само за временно
ползване.
Ето още едно примерче:
Да предположим че сме декларирали променлива със стойност 2 (преди извикването на функцията)
Код: |
1 2 3 4 5 6 7 8 9
| subirane(&a) {
new rezultat = a + a
a = a - 1
return rezultat
}
new a = 2
new rezultat = subirane(a) |
.. сега стойността която a ще приеме дори след извикването на функцията ще е 3.
Ето видяхте ли сега разликите ? .. резултата се запаметява в
променливата (или по-точно се преписва, но както искате си го
запомнете)
Надявам се че съм обяснил добре и сте разбрали чрез примерчетата и обесненията, които дадох какво значат тези два метода.
Та да се върнем на нашата тема с масивите !
При преминаването на масиви през функция, те минават през 2рият метод, за който споменах (чрез преписване / by reference).
Тъй като нормалните променливи преминат през функцията, те се
копират в паметта и копираната част се изпраща до функцията и след това
се трие (и за това не се "запаметява" след използването й)
Но тук с масивите не стоят нещата по този начин. Тъй като масивите
могат да бъдат много големи и затова се преписват, вместо да се
копират.
Това ще рече че ако промените масива във функцията, той ще бъде променен и за след това.
И тук ще дам един пример:
Код: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| // Тази функция ще размени слотовете a и b вътре в който и да е било масив, преминат към функцията
swap_slots(masiv[], a, c)
{
// Обърнете внимание, че е нужно да задържим един от слотовете, преди да ги разменим.
// В противен случай няма как да стане да им разменим стойностите.
// Ако имаме a и c, правим c да е еквивалентна на a, като елиминираме по този начин оригиналната/първоначалната стойност за c
new temp
temp = masiv[c]
masiv[c] = masiv[a]
masiv[a] = temp
}
new myArray[2]
myArray[0] = 5
myArray[1] = 6
swap_slots(myArray, 0, 1)
//myArray[0] ще е 6, а пък myArray[1] ще бъде 5 |
Но можете да предотвратите промяната на масивите, като при декларирането им ги укажите като "константни".
Код: |
1 2 3 4 5 6 7 8
| add_two_from_array(const masiv[], a, c)
{
new first = masiv[a]
new second = masiv[c]
new sum = add_two_from_array(first, second)
return sum
}
// Сега когато използваме функцията ще бъдем сигурни че масива, няма да бъде променен по никакъв начин. |
Тази функция променя преминаващия масив, който предварително е деклариран че е константен
Код: |
1 2 3 4
| nepravilna_funkciq(const array[])
{
array[0] = 0
} |
.. но тъй като знаем че след като сложим const пред името на масива, той не може да бъде променян... Следователно номера няма да мине..
Реших с урока да спра до тук и да направя 3та част, тъй като този стана големичък.
Очаквайте и продължението на този урок !
Преписването е забранено.