Premenné

Bez toho, aby sme si mohli údaje zapamätávať sa programovať nedá. Na zapamätanie nejakých hodnôt nám slúžia premenné. Premennú si môžeme predstaviť ako krabičku, do ktorej vieme všeličo strčiť. Krabičky majú svoje meno a aj svoj typ.

Premennú s menom si môžeme predstaviť ako krabičku s nálepkou. Meno krabičky/premennej slúži na to, aby sme s nimi vedeli pracovať. Napríklad "ulož číslo do tamtej premennej" počítač nepochopí, on by rád počul "ulož číslo do premennej s menom `a'".

Typ premennej zase určuje, čo do nej môžeme vkladať. Do premennej typu int (odvodené od anglického slova integer) môžeme ukladať celé číslo, do premennej typu string (z angličtiny sa dá preložiť ako reťazec) zasa môžeme vkladať reťazce znakov (na stringy potrebujeme knižnicu). Neskôr sa naučíme používať oveľa viac typov premenných, dokonca vyrábať vlastné typy.

Ako premenné používať? Na to, aby sme mohli s premennou niečo robiť, si ju musíme vytvoriť (deklarovať). To sa robí príkazom <typ-premennej> <meno-premennej>; napríklad int cislo;

Do premenných môžeme vkladať priradením, teda pomocou =. Syntax je <meno-premennej> = <výraz>, napríklad a = 4. O výrazoch si povieme viac neskôr, dôležité zatiaľ je, že výraz musí mať nejakú hodnotu. Napríklad výraz 4 má hodnotu 4. Výraz a má hodnotu toho, čo je vnútri premennej/krabičky s menom a. Výraz 4 + 7 má hodnotu 11. Pokiaľ v a je číslo 5, tak výraz a + 5 má hodnotu 10.

Výraz musí mať hodnotu preto, lebo práve táto hodnota sa uloží do premennej: Napríklad keď napíšeme a = 3 + 8; tak do a sa priradí číslo 11.

Cvičenie: Skúste určiť, čo robí nasledovný program. Aké hodnoty budú na konci v premenných a, b, c?

int main(){
    int a;
    a = 4;
    a = 7;

    int b, c;
    b = a;
    c = a + b;
    a = a + 4;
}
Riešenie Najskôr sme vyrobili premennú `a`. Potom sme do nej priradili hodnotu 4. Následne sme do nej priradili hodnotu 7 (stará hodnota 4 sa zahodila), teda premenná bude obsahovať iba číslo 7. Potom sme vyrobili dve nové premenné `b` a `c` (správne, premenných rovnakého typu vieme vyrábať aj viac naraz, stačí ich mená oddeliť čiarkou). Do premennej `b` sa priradila hodnota výrazu `a`, teda 7. Do `c` sa priradí hodnota výrazu `a + b`, čo je hodnotou výrazu `7 + 7` teda 14. Nakoniec sa do `a` priradí `a + 4`, čiže `7 + 4`, čiže 11. V `a, b, c` budú na konci programu postupne hodnoty 11, 7, 14.

Aby sme vedeli lepšie pozorovať, čo sa v programe deje, naučíme sa vypisovať premenné na obrazovku. Bude na to slúžiť príkaz printf(), ale teraz bude použitý zložitejšie. Funkcii printf() totiž môžeme do zátvoriek napísať viac argumentov ako jeden. Prvý argument totiž nemusí byť obyčajný reťazec, ale môže byť špeciálny formátovací reťazec. Od obyčajného sa líši tým, že obsahuje podivné značky ako %d %lf %4d a mnohé ďalšie. Tieto značky sú pri spracovaní funkciou nahradené hodnotami, ktoré určíme v ďalších argumentoch tejto funkcie.

Vyskúšajte si skompilovať a spustiť nasledovný program.

#include <cstdio>
int main() {
    printf("%d + %d = %d\n",4,7,4+7);
}

Mal by vypísať reťazec 4 + 7 = 11. Totiž funkcii printf sme dali 4 argumenty (argumenty oddeľujeme čiarkou) reťazec "%d + %d = %d\n" a tri výrazy 4, 7 a 4+7 s hodnotami 4, 7 a 11. Funkcia najprv zoberie prvý argument, ktorý musí byť reťazec, nájde tam všetky výskyty %<nieco> a nahradí ich hodnotami ďalších argumentov. %d znamená, že argument bude celé číslo, ktoré chceme vypísať v desiatkovej sústave. O iných značkách si povieme viac neskôr, slúžia buď na vypisovanie iných typov objektov (reálne čísla, znaky, reťazce...) alebo na rôzny spôsob ich vypísania (počet desatinných miest, sústava...).

Keďže premenné sú tiež výrazy, tak môžeme vypisovať ich hodnoty takto:

#include <cstdio>

int main() {
    int a;
    a = 17;
    printf("premenna a ma hodnotu %d\n",a);
}

Alebo bez zbytočných blábolov:

#include <cstdio>

int main() {
    int a = 7;
    printf("%d\n",a);
}

Všimnite si riadok int a = 7;. Ide o skrátený zápis dvoch príkazov int a; a = 7. Dá sa to použiť aj pri deklarácií viacerých premenných, teda int a = 1, b = 2, c = 3; je skrátený zápis pre int a = 1; int b = 2; int c = 3;

Cvičenie: Skúste na rôznych miestach programu z cvičenia 1 vypísať hodnoty premenných. Všelijako upravujte program a pozorujte, čo sa deje.

Typy číselných premenných

K číselným premenným treba poznamenať ešte niekoľko dôležitých vecí. Každá číselná premenná a aj každé číslo má obmedzenú veľkosť.

Napríklad premenná typu int môže mať v sebe uložené len celé číslo od $-2147483648$ po $2147483647$. Je to preto, že int je v pamäti zapísaný ako 32 jednotiek a núl. Teda si vieme zapamätať len $2^{32}$ rôznych hodnôt. V prípade premennej typu int sú to hodnoty od $-2^{31}$ po $2^{31}-1$ (záporných čísel je presne polovica, kladných čísel je o 1 menej, kvôli číslu 0).

Cvičenie: Vyskúšajte si spustiť príkaz printf("%d\n", 2147483647 + 1);.

Existujú premenné aj s menším rozsahom, napríklad short int má rozsah $-32768..32767$ alebo $-2^{15}..2^{15}-1$. char má rozsah $-128..127$ resp. $-2^7..2^7-1$. Premenná typu bool má len dve hodnoty false resp. 0 (nepravda) alebo true resp. 1 (pravda).

Väčší rozsah majú zasa premenné long long, od $-2^{63}..2^{63}$. Pri ich vypisovaní však treba dávať pozor, namiesto "%d" treba písať "%lld" na Linuxe alebo "%I64d" na Windowse.

Existujú aj typy premenných, ktoré neukladajú záporné čísla. Napríklad unsigned int má rozsah $0..2^{32}-1$. Pri ich používaní musíme byť obozretní, lebo sa vám môže stať, že mínus jedna je viac ako nula. (Totiž $-1$ v bezznamienkovom inte bude $4294967295$, kvôli tomu, ako sa udržujú hodnoty v pamäti.)

Binárna sústava a reprezentácia dát v pamäti.

Každá hodnota je v pamäti počítača reprezentovaná ako postupnosť núl a jednotiek ­— bitov. Osem bitov je jeden bajt. Napríklad int má veľkosť 4 bajty, čiže 32 bitov. char má zase len jeden bajt ­— 8 bitov. Keď máme v premennej typu char uložené číslo 47, tak v pamäti počítača to vyzerá ako 00101111. Rovnaké číslo v premennej typu int vyzerá 00000000 00000000 00000000 00101111.

Poznámka, niektoré počítače alebo systémy majú iné poradie bitov v pamäti.

Čísla sú teda v pamäti napísané v dvojkovej (binárnej) sústave. Tá je veľmi podobná našej desiatkovej (decimálnej), takže sa jej netreba báť. Aby sme sa neplietli, tak teraz chvíľu budeme písať sústavu do dolného indexu, lebo napr. $11_2 \neq 11_{10}$. (Alebo $3_{10} \neq 11_{10}$)

Keď v desiatkovej sústave napíšeme $325_{10}$ myslíme tým tri stovky, dve desiatky a päť jednotiek. V dvojkovej sústave napr. $1101_2$ je jedna osmička, jedna štvorka, žiadna dvojka a jedna jednotka. Dokopy $8+4+0+1 = 13$, teda $1101_2 = 13_{10}$.

Matematicky zapísané, $325_{10} = 3\cdot 100 + 2\cdot 10 + 5\cdot 1$. Ak chceme napísať $325_{10}$ v dvojkovej sústave, musíme použiť namiesto mocnín desiatky ($1$, $10$, $100$, $1000$, ...) mocniny dvojky ($1$, $2$, $4$, $8$, $16$, ...). Teda $325 = 1\cdot 256 + 0\cdot 128 + 1\cdot 64 + 0\cdot 32 + 0\cdot 16 + 0\cdot 8 + 1\cdot 4 + 0\cdot 2 + 1\cdot 1$. Preto $325_{10} = 101000101_2$.

Opačný prevod sme už zvládli vyššie, napríklad ak nás zaujíma hodnota $101111_2$, stačí jednoducho sčítať $1\cdot 1 + 1\cdot 2 + 1\cdot 4 + 1\cdot 8 + 0\cdot 16 + 1\cdot 32 = 47_{10}$.

Cvičenie: Preveďte z desiatkovej sústavy do dvojkovej nasledovné čísla $31$, $200$, $1024$.

Riešenie $31_{10} = 11111_2$. $200_{10} = 11001000$. $1024_{10} = 10000000000$, lebo $1024 = 2^{10}$.

Cvičenie: Preveďte z dvojkovej sústavy do desiatkovej čísla $10101$, $100000000$, $1100110011$.

Riešenie $10101_2 = 21_{10}$. $100000000_2 = 256$. $1100110011_2 = 819_{10}$

Cvičenie: Sčítajte v binárnej sústave čísla $110110$ a $1111101$. Následne ich preveďte do desiatkovej sústavy a sčítajte v desiatkovej sústave. Výsledok preveďte naspäť do dvojkovej a výsledok overte.

Riešenie V binárnej sústave sčítavame rovnako ako v desiatkovej, napíšeme si čísla pod seba, sčitavame zľava doprava a pripočítavame prípadné zvyšky.
    110110     =     54
+  1111101     =  + 125
   -------          ---
  10110011     =    179

Kladné čísla sú v pamäti reprezentované práve v dvojkovej sústave. Keď je číslo príliš veľké, jednoducho sa zahodia prvé bity ­— ukážeme si to na príklade. Premenná typu unsigned char má jeden bajt (8 bitov) a teda je schopná reprezentovať čísla od 0 po 255. Nie je žiadným prekvapením, že 5 bude zapamätané ako $00000101$, či $255$ ako $11111111$. Čo sa stane, keď ku $255$ pripočítame 1? Dostaneme $11111111 + 1 = 100000000$. Keďže si môžeme zapamätať len 8 bitov, musíme prvú jednotku zahodiť. Dostaneme $00000000$, čo je 0.

Preto pri premennej unsigned char $255+1 = 0$ alebo $100\cdot 100 = 16$.

Ale ako je to s premennými ktoré podporujú aj záporné čísla? Kladné čísla sa reprezentujú tak ako predtým, napríklad v premennej typu char $0 = 00000000$ až $127 = 01111111$.

Čo sa stane, keď od nuly odpočítame 1? $00000000 - 1 = ...11111111111111$ (nekonečné čislo) ale nás zaujíma len posledných 8 bitov, čo je $11111111$. Takže $-1 = 11111111$. Keď ďalej odpočítavame 1 dostávame $-2 = 11111110$, $-3 = 11111101$ ... $-128 = 10000000$.

Super je, že sa nám zachovali logické vlastnosti, napríklad že $-1 + 1 = 0$, $-50 + 40 = -10$, $-31 + 100 = 69$... bez toho, aby sme museli pri práci s binárnym čislom rozlišovať, či je číslo záporné alebo kladné. Jednoducho sčítame čísla pod sebou a ak sa výsledok nezmestí, tak zahodíme prvé bity.

Napr. $-1 + (-1) = 11111111 + 11111111 = 1011111110 \rightarrow 11111110 = -2$.

Tento spôsob reprezentovania záporných čísel sa nazýva aj dvojkový doplnkový kód.

Vtip: "Ľudí môžeme rozdeliť do 10 skupín ­— tých, ktorí rozumejú binárnej sústave a tých, ktorí jej nerozumejú."

Pretypovanie

Sčítavať, odčítavať, násobiť, priraďovať, porovnávať... sa dajú len premenné a čísla rovnakých typov. (Všimnite si, že aj čísla (napríklad číslo 45) majú svoje typy.)

Pokiaľ sú pri operácii typy rôzne, treba ich pretypovať a naštastie v C++ sa mnohé pretypovanie robí automaticky. Napríklad medzi číslami.
int a = 47; char c = a; nie je žiaden veľký problém. (V tajnosti prebehne jedno pretypovanie, v príkaze c = a, sa a vyhodnotí ako (int)47, pretypuje sa na (char)47 a následne sa priradí i c = (char)47.

Pokiaľ pretypujeme z menších premenných do väčších, číslo sa doplní nulami zľava. (char)19 $= 00010011 \rightarrow 00000000 00000000 00000000 00010011 =$ (int)19

Z väščích do menších, sa zasa zahadzujú prvé bity ­— pozor pri tomto pretypovaní sa môže číslo zmeniť. (int)200 $= 00000000 00000000 00000000 11001000 \rightarrow 11001000 =$ (char)(-56) Tento jav sa volá pretečenie.

Bežné čísla, ktoré napíšete v programe majú typ int. Teda aj pri char a = 20; nastáva pretypovanie z int do char.

Keď niekde napíšete číslo $123\,456\,789\,123$, tak vám pravdepodobne kompilátor vypíše varovanie, lebo dané číslo je typu int, ale nezmestí sa do jeho rozsahu. Treba písať 123465789123LL alebo 123456789123ll.

Pri operáciach ako sčítanie, násobenie... na rôznych typoch sa najprv obe čísla pretypujú na všeobecnejší typ, až potom sa spočítajú.

Napríklad ak char a = 20; a int b = 20; tak výsledkom a * b bude 400.

Treba si dávať pozor na nasledovnú vec.

Cvičenie: Skopírujte, skompilujte a spustite nasledovný program. (Pokiaľ používate windows, použite miesto %lld %I64d)

#include<cstdio>
int main(){
    ll a = 1000000*1000000;
    printf("d = %lld\n",d);
}

Čo vypísal? Prečo? Ako by ste ho opravili, aby naozaj vypísal $1\,000\,000\,000\,000$?

Riešenie Vynásobili sme dva `int`y, keďže majú rovnaký typ, nič sa nepretypuje. Keďže výsledok je príliš veľký, nastáva pretečenie a výsledkom násobenia bude `int` $-727\,379\,968$. Následne sa vykoná priradenie `a = -727379968`, ale správna hodnota už je nenávratne preč. Opraviť to môžeme tak, že zmeníme `1000000` na `1000000LL`. (Stačí zmeniť jedno.)

Pretypovanie bežne funguje automaticky, ale vieme ho spôsobovať aj ručne. Syntax je (<typ>)<hodnota> alebo <typ>(hodnota) alebo použijeme obe dvojice zátvoriek.

Cvičenie: Aké budu hodnoty v a, b po nasledovných priradeniach?

int a = 1234, b;
b = (char)a;
b = char(a);
b = char(a)*a;
Riešenie Hodnota `a` bude vždy $1234$, pretypovanie nemení premenné. `b` bude mať hodnoty -46, -46 a -56764 (teda $-46\cdot 1234$).

Poznámka, bool nie je číselný typ. Teda bool(int(4)) nie je 0 hoci posledný bit v zápise 4 je 0. Pre bool platí, že bool(0) je false (resp. 0), všetky ostatné čísla sa zmenia na true (resp. 1).

Ku číslam sa hodí spomenúť ešte dve číselné operácie, delenie / a zvyšok po delení %.

Keď pracujeme s celými číslami, tak chceme aby aj podiel bol celé číslo. Preto sa výsledok vždy zaokrúhľuje smerom k nule. $3/2$ nie je $1.5$ ale $3/2 = 1$, $11/3 = 3$, $14/7 = 2$, $-15/3 = -5$, $-100/3 = -33...$

Zvyšok po delení určite poznáte so základnej školy, dá sa definovať ako $a \% b = a - (a/b)\cdot b$. Napríklad $47 \% 10 = 47 - 47/10\cdot 10 = 47 - 4\cdot 10 = 47 - 40 = 7$, $-5 \% 3 = -2$.