Varijable i tipovi podataka

Što je varijabla i kako je koriste micro:bit i Arduino: tipovi int, long, float, char i bool, njihovi rasponi, const te česte greške s brojevima.

Arduino Info Micro:bit

Kutijica s imenom

Program mora pamtiti mnogo toga: koliko bodova imaš, koliko ti je života ostalo, traje li igra još. Za to služi varijabla: kutijica s imenom u koju program spremi jedan podatak.

Tri otvorene kutije s natpisima. U kutiji bodovi je kartica s brojem 3, u kutiji zivoti kartica s brojem 2, a u kutiji igraTraje kartica s riječju true

Ime kutijice ostaje isto, a ono što je u njoj program smije mijenjati dok radi. Zato se i zove varijabla: na latinskom variare znači mijenjati.

S varijablom program radi tri stvari:

  1. Napravi je i da joj ime, na primjer bodovi.
  2. Spremi u nju vrijednost. Ono što je prije bilo u kutijici nestane, jer kutijica pamti samo zadnju vrijednost.
  3. Pročita što je u njoj, na primjer da broj bodova prikaže na zaslonu.

Na kojem jeziku pišemo program za Arduino?

Program za Arduino pišemo u programskom jeziku C++ (čita se ce plus plus). C++ je nastao početkom 1980-ih kao nadogradnja jezika C, a danas se u njemu pišu igre, preglednici i programi za automobile i rakete.

Arduino jeziku C++ dodaje gotove funkcije, kao pinMode(), digitalWrite() i delay(), i dvije funkcije koje svaki program mora imati, setup() i loop(). Pravi program u jeziku C++ počinje funkcijom main(). Arduino je napiše sam: ona jednom pozove setup(), a zatim loop() ponavlja zauvijek.

I blokovi u MakeCodeu su program u pravom programskom jeziku, JavaScriptu. Na vrhu MakeCodea klikni JavaScript, ili u izborniku pokraj njega Python, pa ćeš vidjeti isti program napisan riječima.

Vrste podataka

U jednu varijablu stane samo jedna vrsta podatka:

  • broj, na primjer broj bodova ili temperatura;
  • tekst, na primjer ime igrača;
  • istina ili laž: true (da) ili false (ne), na primjer traje li igra.

MakeCode vrstu prepozna sam, po prvoj vrijednosti koju spremiš u varijablu. Ako u bodovi na početku spremiš 0, to je varijabla za brojeve, pa u nju poslije spremaj samo brojeve.

Kako se varijable koriste na micro:bitu, piše u odjeljku Varijable u MakeCodeu, a za Arduino radionicu je odjeljak Varijable u Arduino programu.

Varijable u MakeCodeu

Varijable su u kategoriji Variables:

  1. Klikni Make a Variable…, upiši ime i klikni OK.
  2. Blokom set bodovi to 0 spremiš vrijednost. Najčešće ga staviš u on start, da igra počne od nule.
  3. Blok change bodovi by 1 poveća vrijednost za 1. S −1 je smanji, kao kad igrač izgubi život.
  4. Okrugli blok bodovi pročita vrijednost. Staviš ga u neki drugi blok, na primjer u show number.

Varijable smo prvi put koristili u lekcijama Moj prvi program u MakeCodeu i Kako naš program razmišlja?, za bodove. U lekciji Životi i uvjeti varijabla zivoti broji unatrag, a igraTraje pamti samo true ili false. U Radio igrama varijabla protivnik na početku ima vrijednost −1, jer takav znak ne postoji: tako program zna da protivnikov znak još nije stigao.

Dobro ime za varijablu

  • Neka ime kaže što je u varijabli: bodovi, a ne x. Za tjedan dana nećeš se sjećati što je bio x.
  • Bez razmaka i bez slova č, ć, đ, š i ž: zivoti, igraTraje. Kad ime ima više riječi, svaku novu riječ započni velikim slovom.
  • Velika i mala slova su različita: bodovi i Bodovi su dvije različite varijable.

Varijable u Arduino programu

Od ovog mjesta stranica je za Arduino radionicu.

Kako napraviti varijablu

U C++ programu varijablu napravimo jednim retkom. Napišemo kojeg je tipa, kako se zove i, ako želimo, što je u njoj na početku:

int bodovi = 0;          // tip, ime i početna vrijednost
bool igraTraje = false;  // istina ili laž
float napon = 4.85;      // decimalni broj: s točkom, a ne sa zarezom

To se zove deklaracija. Poslije ime pišemo bez tipa:

bodovi = 10;             // spremi 10, a stara vrijednost nestane
bodovi = bodovi + 1;     // pročitaj, dodaj 1 i spremi natrag: sada je 11
bodovi++;                // isto, kraće: 12
bodovi += 5;             // dodaj 5: 17

Znak = ne znači usporedbu jednakosti, nego spremi: vrijednost s desne strane spremi u varijablu na lijevoj. Zato bodovi = bodovi + 1 nije greška u računu. Jesu li dvije vrijednosti jednake, pitamo sa ==.

= nije isto što i ==

if (bodovi = 10) ne pita je li bodovi jednako 10, nego u bodovi spremi 10. Uvjet je tada uvijek ispunjen, a stari broj bodova je izgubljen. Prevoditelj to ne javi kao grešku, pa u uvjetu uvijek provjeri da su napisana dva znaka jednakosti: if (bodovi == 10).

Za imena vrijede ista pravila kao u MakeCodeu, i još nekoliko:

  • Ime smije imati slova, brojke i podvlaku _, ali ne smije početi brojkom: led2 može, 2led ne može. Slova č, ć, đ, š i ž Arduino u imenima ne prihvaća (pogledaj lekciju Semafor za automobile i pješake).
  • Ime ne smije biti riječ koju C++ već koristi, kao int, if ili for.
  • Izbjegavaj imena Arduino funkcija i funkcija jezika C, kao time, random, link ili signal. Na nekim Arduinima takvo ime izazove grešku pri prevođenju.

Tipovi podataka

Svaki tip zauzme zadan broj bajtova memorije, pa u njega stanu samo brojevi iz zadanog raspona. Na Arduinu Uno R3, Nanu i Megi, koji imaju 8-bitni mikrokontroler, izgleda ovako (jedan kvadratić je jedan bit):

Za svaki tip red kvadratića, po osam u bajtu: bool, char i byte imaju po jedan bajt, int i unsigned int po dva, a long, unsigned long i float po četiri bajta
TipBajtovaRasponZa što ga koristimo
bool1true ili falseda ili ne: je li tipkalo pritisnuto, traje li igra
char1−128 do 127jedan znak, na primjer 'A' ili '#'
byte10 do 255mali brojevi i bajtovi, na primjer redak slike na LED matrici
int2−32 768 do 32 767većina brojeva: pinovi, brojači, analogRead()
unsigned int20 do 65 535cijeli brojevi bez predznaka, do 65 535
long4−2 147 483 648 do 2 147 483 647veliki cijeli brojevi
unsigned long40 do 4 294 967 295vrijeme iz millis()
float4oko ±3,4 · 1038, 6 do 7 znamenkidecimalni brojevi: napon, temperatura

Unsigned znači bez predznaka: takav tip nema negativnih brojeva, pa mu je najveći broj dvostruko veći. Zašto je baš tako, objašnjava stranica Binarni, oktalni i heksadecimalni brojevi.

Kako odabrati tip:

  • Za većinu brojeva dovoljan je int.
  • Vrijeme iz millis() uvijek spremaj u unsigned long, jer int napuni za 33 sekunde (lekcija Dva semafora na križanju). Iz istog razloga u lekciji Tipkala kako treba brojač koji brzo raste dobije unsigned long.
  • Za da ili ne koristi bool, kao ledMode u lekciji Djelilo napona, Arduino i PWM.
  • Znakove tipkovnice sprema char, kao u lekciji Mnogo prekidača na malo pinova.
  • float koristi samo kad trebaš decimale. Uno nema sklop za računanje s decimalnim brojevima, pa je s njima sporiji, a float pamti samo 6 do 7 znamenki: 1234,5678 spremi kao otprilike 1234,568.

Nije svaki int jednak

Rasponi u tablici vrijede za 8-bitne mikrokontrolere. Na Unu R4, Gigi i ESP32, koji imaju 32-bitni mikrokontroler, int ima 4 bajta, kao long, i u njega stanu brojevi do 2 147 483 647. Program koji na Unu R3 napuni int tamo zato radi drukčije.

Kad broj mora imati točno zadan broj bitova na svakom mikrokontroleru, koristi tipove kojima je broj bitova u imenu: int16_t (16 bitova s predznakom), uint8_t (8 bitova bez predznaka, isto što i byte) ili uint32_t (32 bita bez predznaka).

Vrijednost koja se ne mijenja: const

Riječ const ispred tipa kaže da se vrijednost ne mijenja dok program radi:

const int ledPin = 9;                  // pin na koji je spojena LED-ica
const unsigned long SMIRIVANJE = 50;   // broj s posebnim značenjem: velikim slovima

Ako program pokuša promijeniti takvu vrijednost, prevoditelj javi grešku. Tako se ne može dogoditi da program slučajno promijeni broj pina. U lekcijama pinove pišemo kao ledPin, a brojeve s posebnim značenjem velikim slovima, kao TREPCE u lekciji o semaforu za pješake i SMIRIVANJE u lekciji o tipkalima.

Gdje živi varijabla

Gdje u programu napišeš varijablu, određuje koliko dugo postoji i tko je smije koristiti:

  • Varijabla iznad setup() (globalna) nastane kad se program pokrene i pamti svoju vrijednost dok program radi. Smije je koristiti svaka funkcija. Ako joj ne zadaš početnu vrijednost, na početku je 0.
  • Varijabla unutar funkcije, na primjer unutar loop() (lokalna), nastane iznova pri svakom pozivu i nestane na kraju funkcije. Smije je koristiti samo ta funkcija, a ako je napisana unutar vitičastih zagrada { }, samo dio programa unutar njih.

Lokalna varijabla bez početne vrijednosti

Lokalna varijabla bez početne vrijednosti nije 0: u njoj je ono što je slučajno ostalo u memoriji. Zato je napiši s početnom vrijednošću, int zbroj = 0;, a ne samo int zbroj;.

Kada koju koristiti, objašnjava okvir Gdje pišemo varijable? u lekciji Dva semafora na križanju.

Računanje s cijelim brojevima

Kad dijeliš dva cijela broja, i rezultat je cijeli broj. Ostatak se odbaci, a ne zaokruži: 7 / 2 je 3, a 9 / 10 je 0. Ostatak dijeljenja daje znak %: 7 % 2 je 1.

Decimalni rezultat dobiješ samo ako je barem jedan broj decimalni: 7 / 2.0 je 3,5. Nije dovoljno rezultat spremiti u float. U retku float x = 7 / 2; Arduino prvo podijeli dva cijela broja, a tek onda 3 spremi u x, pa je x 3,00.

Kad broj prijeđe najveću vrijednost svog tipa, počne ispočetka: byte od 255 nakon + 1 ima 0, a int od 32 767 skoči na −32 768. Najteže se uoči kad se to dogodi usred računa:

unsigned long krivo = 60 * 1000;    // 4294961760, a ne 60000
unsigned long dobro = 60 * 1000UL;  // 60000

Arduino 60 i 1000 pomnoži kao dva int-a, a 60 000 ne stane u int. Pokvareni rezultat spremi tek na kraju, pa čekanje od jedne minute traje 49 dana. Oznaka UL iza broja kaže da je broj unsigned long, pa se cijeli račun izvede u tom tipu. Prevoditelj javi samo upozorenje integer overflow in expression, i to samo ako su upozorenja uključena: u Arduino IDE-u u File → Preferences → Compiler warnings.

Probaj sam

Ovaj program pokazuje sve zamke s ove stranice. Učitaj ga, otvori serijski monitor i usporedi ispis s komentarima:

void setup() {
  Serial.begin(9600);

  int a = 7;
  int b = 2;
  Serial.println(a / b);            // 3: cijeli brojevi, ostatak se odbaci
  Serial.println(a % b);            // 1: ostatak dijeljenja
  Serial.println(a / 2.0);          // 3.50: 2.0 je decimalni broj
  float krivo = a / b;              // dijeli cijele brojeve, a tek onda sprema u float...
  Serial.println(krivo);            // ...pa je 3.00

  byte brojac = 255;                // najveći byte
  brojac = brojac + 1;
  Serial.println(brojac);           // 0: broj je počeo ispočetka

  unsigned long krivoVrijeme = 60 * 1000;    // 60 i 1000 su int, a 60000 ne stane u int
  unsigned long dobroVrijeme = 60 * 1000UL;  // UL: računaj kao unsigned long
  Serial.println(krivoVrijeme);     // 4294961760
  Serial.println(dobroVrijeme);     // 60000

  char slovo = 'A';
  Serial.println(slovo);            // A
  Serial.println(slovo + 1);        // 66: broj sljedećeg znaka
  Serial.println(char(slovo + 1));  // B
}

void loop() {
}

Zadnja tri retka pokazuju da je i char zapravo broj: slovo A ima broj 65, B 66 i tako dalje. Serial.println() znak ispiše kao slovo samo dok je tipa char. slovo + 1 je već int, pa ga ispiše kao broj, a char(...) ga pretvori natrag u znak.

Serial.println() decimalni broj ispiše s dvije decimale. Za više decimala dodaj njihov broj: Serial.println(napon, 4);.

Što smo naučili?

  • Varijabla ima ime, a u njoj program sprema jedan podatak. Varijabla pamti samo zadnju vrijednost.
  • U MakeCodeu varijable su u kategoriji Variables, a vrstu podatka MakeCode prepozna sam. U Arduino programu tip napišemo ispred imena: int bodovi = 0;.
  • = sprema vrijednost, a == pita jesu li dvije vrijednosti jednake.
  • Svaki tip ima zadan broj bajtova i raspon. Za većinu brojeva dovoljan je int, za vrijeme unsigned long, za da ili ne bool, a float samo kad trebamo decimale.
  • const čuva vrijednost od promjene. Globalna varijabla pamti vrijednost dok program radi, a lokalna nastaje i nestaje s funkcijom.
  • Dijeljenje dva cijela broja odbaci ostatak, a broj koji prijeđe raspon svog tipa počne ispočetka, i usred računa.

Mali kviz

1. Što je varijabla?
Varijabla ima ime, a u njoj program sprema jedan podatak, na primjer broj bodova. Ime varijable ostaje isto, a njezina vrijednost se smije mijenjati.
2. U varijablu bodovi spremiš 5, a zatim 8. Što je u njoj?
  1. Varijabla pamti samo zadnju vrijednost, pa 5 nestane.
3. Zašto se varijabla ne smije zvati životi?
Zbog slova ž. U imenima ne koristimo slova č, ć, đ, š i ž, pa pišemo zivoti.
4. Koji tip odabrati za vrijeme iz millis()?
unsigned long. int napuni već za 33 sekunde, a u unsigned long stane vrijeme od 49 dana.
5. Koliko je 9 / 4 u Arduino programu, a koliko 9 % 4?
9 / 4 je 2, jer se ostatak cijelih brojeva odbaci. 9 % 4 je 1, ostatak dijeljenja.
6. Što radi if (stanje = 3)?
U varijablu stanje spremi 3, a uvjet je uvijek ispunjen. Program je htio pitati if (stanje == 3).
7. Zašto unsigned long minuta = 60 * 1000; ne daje 60000?
60 i 1000 su int, pa ih Arduino pomnoži kao int, a 60 000 ne stane u int. Pokvareni rezultat spremi tek na kraju. Ispravno je 60 * 1000UL.
8. Lokalna varijabla int zbroj; napisana je bez početne vrijednosti. Koliko je zbroj?
Ne zna se: u njoj je ono što je slučajno ostalo u memoriji. Zato pišemo int zbroj = 0;. Globalna varijabla bez početne vrijednosti bila bi 0.