Programovanie (1) v C/C++
1-INF-127, ZS 2017/18

Úvod · Pravidlá · Prednášky · Odovzdávanie
Stránku pre školský rok 2017/18 pripravujeme. Materiály z minulého roku nájdete v archíve.
· Vyučujúcich môžete kontaktovať pomocou e-mailovej adresy E-prg.png (bude odpovedať ten z nás, kto má príslušnú otázku na starosti alebo kto má práve čas).
· Prosíme študentov, aby si pravidelne čítali e-maily na @uniba.sk adrese alebo aby si tieto emaily presmerovali na adresu, ktorú pravidelne čítajú, viď návod tu: [1]


Prednáška 2

Z Programovanie
Prejsť na: navigácia, hľadanie

Premenné

Tradičný preškrtnutý domček

Spomeňme si na program na vykresľovanie domčeka z minulej prednášky:

#include "../SVGdraw.h"

int main(void) {
    SVGdraw drawing(200, 300, "domcek1.svg");

    drawing.drawRectangle(50, 150, 100, 100);

    drawing.drawLine(50, 250, 150, 150);
    drawing.drawLine(50, 150, 150, 250);

    drawing.drawLine(50, 150, 100, 50);
    drawing.drawLine(150, 150, 100, 50);

    drawing.finish();
}

Ak by sme v ňom chceli zmeniť napríklad výšku domčeka, museli by sme pomeniť veľa súradníc v celom programe. Navyše keď vidíme v programe nejaké číslo, napr. 250, nevieme, ako sme k nemu prišli.

Program na kreslenie domčeka teraz prepíšeme tak, aby sme polohu a veľkosť domčeka mali zapísané symbolicky a mohli ich meniť na jednom mieste.

#include "../SVGdraw.h"

int main(void) {
    /* x a y sú súradnice ľavého horného rohu domčeka,
     * width a height sú jeho šírka a dĺžka,
     * roof je výška strechy. */
    int x = 50;
    int y = 150;
    int width = 100;
    int height = 100;
    int roof = 100;

    /* Vytvor obrázok s rozmermi 200x300 pixelov a 
     * ulož ho do súboru domcek1.svg*/
    SVGdraw drawing(200, 300, "domcek1.svg");

    /* Nakresli obdĺžnik (štvorec) s ľavým horným rohom v 50, 150
     * a šírkou aj dĺžkou 100. */
    drawing.drawRectangle(x, y, width, height);

    /* Prečiarkni štvorec dvomi čiarami po uhlopriečke. */
    drawing.drawLine(x, y+height, x+width, y);
    drawing.drawLine(x, y, x+width, y+height);

    /* Nakresli strechu ako dve čiary. */
    drawing.drawLine(x, y, x+width/2, y-roof);
    drawing.drawLine(x+width, y, x+width/2, y-roof);

    /* Ukonči vypisovanie obrázka. */
    drawing.finish();
}

Symbolickým hodnotám x,y,width,height,roof sa hovorí premenné.

  • Premmená je určité vyhradené miesto v pamäti počítača, ku ktorému v programe pristupujeme pod určitým názvom.
  • Do tejto pamäti si môžeme zapísať hodnotu a neskôr ju použiť.
  • Príkaz int x=100; vytvorí novú premennú a uloží do nej hodnotu 100.
  • Každá premenná má určitý typ, ktorý určuje, aké hodnoty do nej môžeme ukladať.
  • Tieto premenné majú typ int, čo je skratka zo slova integer, celé číslo.
Domček s height=200, roof=50

Ak v programe premenným priradíme iné čísla, môžeme vytvárať domčeky, ktorú budú vyššie alebo širšie, alebo budú mať strechu inej výšky, napr:

    int x = 100;
    int y = 300;
    int width = 100;
    int height = 200;
    int roof = 50;

Príkaz int x=100; vieme rozpísať aj na dva príkazy int x; x=100;. Prvý z nich vytvorí premennú x, ktorá teraz bude mať nejakú ľubovoľnú hodnotu a druhý túto počiatočnú hodnotu zmení na 100.

Cvičenie: v programe sa opakuje vzorec na výpočet x-ovej súradnice vrcholu strechy. Spočítajte ho iba raz a uložte do premennej middle

Textový výpis a načítanie

Vieme už vypísať niečo na obrazovku (výstup - output) a podobne môžeme aj čítať, čo nám používateľ napíše na klávesnici (vstup - input). Takéto zadané hodnoty tiež uložíme do premenných, aby sme s nimi mohli ďalej pracovať.

Nasledujúci program od užívateľa vypýta dve čísla a vypíše ich súčet.

#include <iostream>
using namespace std;

int main(void) {
    int x, y;

    cout << "Please enter the first number: ";
    cin >> x;
    cout << "Please enter the second number: ";
    cin >> y;

    int result = x + y;
    cout << x << "+" << y << "=" << result << endl;
}

Tu je príklad behu programu, keď užívateľ zadal čísla 10 a 3:

Please enter the first number: 10
Please enter the second number: 3
10+3=13
  • Tento program používa na vstup a výstup príkazy z knižnice iostream a teda do hlavičky programu dáme #include <iostream> a using namespace std;
  • Program najskôr vytvorí dve premenné x a y typu int (a nepriradzuje im zatiaľ žiadne hodnoty)
  • Potom príkazom cout vypíše text "Please enter the first number: " aby užívateľ vedel, čo má robiť.
  • Potom pomocou príkazu cin načíta číslo od používateľa do premennej x
  • To isté opakuje pre premennú y
  • Potom vytvorí novú premennú result a uloží do nej súčet x a y.
  • Nakoniec vypíše výsledok aj s výrazom, ktorý sme počítali, pomocou príkazu cout.

Viac o príkaze cout

  • Pomocou cout vypisujeme na konzolu, t.j. textovú obrazovku
  • To, čo chceme vypísať pošleme na cout pomocou šípky <<
  • cout << endl; vypíše koniec riadku
  • Môžeme naraz vypísať aj viac vecí oddelených šípkami <<
    • Napr. cout << x << "+" << y << "=" << result << endl; vypíše najskôr obsah premennej x (napr. hodnotu 10), potom znamieko plus (ktoré máme v úvodzovkách), potom obsah premennej y, potom znamienko rovnosti, potom obsah premennej result a nakoniec koniec riadku.

Viac o príkaze cin

  • Pomocu cin načítavame z konzoly údaje od užívateľa
  • Tieto údaje pošleme do premenných pomocou šípky >>
  • Opäť môžeme načítať aj viac vecí naraz, napr. nasledovný úryvok si vypýta obe čísla naraz a uloží ich do premenných x a y
   cout << "Please enter two numbers separated by space: ";
   cin >> x >> y;
  • Pozor, cin nekontroluje, že užívateľ zadáva rozumné hodnoty. Čo sa stane, ak namiesto čísla zadá nejaké písmená a podobne?

Výrazy

Pri využívaní premenných by sme si mali niečo povedať aj o výrazoch a ich vyhodnocovaní. Na vytváranie výrazov v programe môžeme používať aritmetické a logické výrazy, zátvorky, čísla, konštanty a premenné.

Premenné

  • Pre začiatok budeme pracovať s premennými typu int a double.
  • Premenná typu int reprezentuje celé číslo.
  • Premenná typu double reprezentuje reálne číslo.
  • Ich rozsah je však obmedzený.
    • Typ int väčšinou zaberá 4 bajty (32 bitov) pamäte a vie ukladať čísla z intervalu <-2 147 483 648, +2 147 483 647>
    • Typ double väčšinou zaberá 8 bajtov a je uložený vo formáte s pohyblivou rádovou čiarkou, t.j. vo forme z\cdot a\cdot 2^{b}, kde z je znamienko, a je reálne číslo z intervalu <1,2) (mantisa) a b je celé číslo (exponent). Na uloženie mantisy sa používa 52 bitov a na uloženie exponentu 11 bitov. Vieme teda spracovávať zhruba čísla v rozsahu od 10^{{-300}} po 10^{{300}} s presnosťou na 15 až 16 platných cifier.
  • Keď priradíme hodnotu typu double do hodnoty typu int, dôjde k jej zaokrúhleniu (nadol pri kladných číslach, nahor pri záporných).

Aritmetické výrazy

  • +, -, * (násobenie), / (delenie)
    • delenie celočíselných premenných vráti dolnú celú časť podielu, napr. 5/3 je 1 (pre záporné čísla to môže byť horná celá časť)
  •  % je modulo, napr. 5%3 bude 2, lebo 5 má zvyšok 2 po delení 3
  • ďalšie matematické funkcie vyžadujú #include <cmath> v hlavičke programu
    • napríklad cos(x), sin(x), tan(x) (tangens), acos(x) (arkus kosínus), exp(x) (e^{x}), log(x) (prirodzený logaritmus), pow(x,y) (x^{y}), sqrt(x) (odmocnina), abs(x) (absolútna hodnota), floor(x) (dolná celá časť)
    • pozri tiež zoznam tu

Skratky: Ak chceme zvýšiť hodnotu premennej x o 1, môžeme použiť niektorý z nasledujúcich spôsobov:

  • x = x+1;
  • x++;
  • x += 1;

Podobne ako ++ existuje aj --, podobne ako += existuje aj -=, *= atď

  • Aritmetické výrazy sa často správajú odlišne pri rôznych operandoch. Peknou ukážkou je delenie.
int i=3; int j=2; double d=2;
cout<<i/j<<endl;    // celociselne delenie 3/2
cout<<i/d<<endl;    // necelocislene delenie 3/2 - jedno je desatinne cislo
   
double c=i/j;
cout<<c<<endl;      // c je sice desatinne cislo ale bolo do neho priradene cele cislo
cout<<c/3<<endl;    // ale uz s nim pracujeme ako s desatinnym
   
double b=(1.0*i)/j; //toto delenie uz nie je celociselne
cout<<b<<endl;   

double a=((double)i)/j; //iny sposob ako dosiahnut necelociselne delenie
cout<<a<<endl;   

Konštanty

  • Celočíselné konštanty, ako napríklad 0, 1, 100, -5, majú typ int
  • Konštanty s desatinnou bodkou, ako napríklad 1.5, 1.0, 3.13, -0.5 majú typ double. Môžeme tiež používať semilogaritmický zápis typu 1.5e3, čo znamená 1.5\cdot 10^{3}, t.j. 1500.

Logické konštanty a výrazy

  • Logické konštanty: true (1) a false (0)
  • Logické výrazy dávajú ako výsledok pravdivostnú hodnotu -- logickú konštantu
    • == (rovnosť), != (nerovnosť), <, <=
    • && (logický AND), || (logický OR), ! (logický NOT)

Vyhodnocovanie výrazov

Výrazy sa vyhodnocujú s preferenciou podľa nasledovnej tabuľky. Výrazy v jednom riadku tabuľky majú rovnakú prednosť a vyhodnocujú sa väčšinou zľava doprava, okrem !,++,-- a priradenia.

  • ++, --, logický NOT
  • *, /, %
  • +, -
  • <, >, <=, >=
  • ==, !=
  • && (logický AND)
  • || (logický OR)
  • priradenie

Poradie vyhodnocovania môžeme meniť zátvorkami, napr. 4*(5-3)

Viac o operátoroch v C++ nájdete napríklad tu.

Podmienka (if)

Niekedy chceme vykonať určité príkazy len ak sú splnené nejaké podmienky. To nám umožňuje príkaz podmienky if.

  • Nasledujúci program si vypýta od užívateľa číslo a vypíše, či je toto číslo párne (even) alebo nepárne (odd).
 #include <iostream>
using namespace std;

int main(void) {
    int x;
    cout << "Please enter some number: ";
    cin >> x;

    if (x % 2 == 0) {
        cout << "Number " << x << " is even." << endl;
    } else {
        cout << "Number " << x << " is odd." << endl;
    }

}
  • Tu je príklad dvoch behov programu:
Please enter some number: 10
Number 10 is even.
Please enter some number: 3
Number 3 is odd.
  • Ako vidíme, za príkazom if je zátvorka s podmienkou. V našom príklade podmienka je x % 2 == 0. Zoberieme teda hodnotu x, pomocou operátora % zistíme zvyšok po delení 0 a pomocou dvoch rovnítok == testujeme, či je tento zvyšok rovný nule.
  • Ak je podmienka v zátvorke splnená (t.j. ak je zvyšok rovný nule), vykonáme príkazy v zloženej zátvorke za príkazom if.
  • Ak podmienka nie je splnená (t.j. ak zvyšok nie je rovný nule), vykonáme príkazy v zloženej zátvorke za slovom else
  • Časť else {...} je možné vynechať, ak nechceme vykonávať žiadne príkazy.
  • Nateraz píšeme zátvorky { a } za if aj za else. Ak za nimi nasleduje iba jeden príkaz, môžeme ich vynechať, ľahko to však vedie k chybám, preto je lepšie ich vždy použiť.

Vnorené podmienky

Pritom príkazy if môžeme navzájom vnárať, čím vzniknú vcelku komplikované výrazy.

  • Načítaj čislo a zisti, či je kladné, záporné alebo nula.
#include <iostream>
using namespace std;

int main(void) {
    int x;
    cout << "Please enter some number: ";
    cin >> x;

    if (x == 0) {
        cout << "Null" << endl;
    } else {
        if (x > 0) {
            cout << "Positive" << endl;
        } else {
            cout << "Negative" << endl;
        }
    }

}

Cvičenie: vypíšte, koľko z čísel 2,3,5 delí zadané číslo x. Napr.

Zadajte cislo: 6
Pocet delitelov z mnoziny {2,3,5}: 2

Zadajte cislo: 60
Pocet delitelov z mnoziny {2,3,5}: 3

Zadajte cislo: 7
Pocet delitelov z mnoziny {2,3,5}: 0

Upozornenie

Častá chyba, ktorá sa vyskytuje pri podmienke je použitie priradenia namiesto porovnania. Keby sme napísali

if (x=0) cout << “Null” << endl; 

tak program do premennej x priradí nulu, ktorá sa premení na logické false pre účely vyhodnotenia podmienky.

Ďalšia bežná chyba je zabudnutie zložených zátvoriek

   if (x==0) cout << “Null”; cout << endl;

Tento program vykoná cout << endl vždy, nezávisle od podmienky. V prípade, že chceme vykonať v podmienke viacero príkazov, nesmieme zabudnúť ich uzátvorkovať:

   if (x==0) { cout << “Null”; cout << endl; }

Cyklus (for)

Teraz si ukážeme príkaz for, ktorý nám umožňuje opakovať viackrát nejakú skupinu príkazov.

Na úvod trochu motivácie. Ako by ste napísali nasledovné jednoduché programy?

  • Vypíšte čísla od 0 do 9. A následne od 0 do 24. (pre vytrvalých od 0 do 99)
  • Vykreslite pravidelný štvoruholník. Šesťuholník? Devätnásťuholník?

Vypisovanie čísiel

Nasledujúci program vypíše čísla 0 až 9 oddelené medzerami bez toho, aby sme ich niekde v programe explicitne vymenovali.

#include <iostream>
using namespace std;

int main(void) {
    for (int i = 0; i < 10; i++) {
        cout << " " << i;
    }
    cout << endl;
}

Tu je výstup programu.

0 1 2 3 4 5 6 7 8 9

Ak by sme v programe číslo 10 zmenili napr. na 25, vypíše čísla 0 do 24.

  • Novou črtou tohto programu je príkaz for pre cyklus: for (int i = 0; i < 10; i++)
  • Cyklus nám umožňuje opakovať určitú časť programu viackrát.
  • V zátvorke za for sú tri časti oddelené bodkočiarkami.
    • Príkaz int i = 0 vytvorí novú celočíselnú premenú i a priradí jej hodnotu 0.
    • Podmienka i < 10 určuje dokedy sa má cyklus opakovať, t.j. kým hodnota i je menšia ako 10.
    • Príkaz i++ hovorí, že v každom kroku sa má premenná i zvýšiť o jedna.
  • Medzi zložené zátvorky { a } môžeme dať jeden alebo viac príkazov, ktoré sa budú opakovať pre rôzne hodnoty premennej i.
    • V našom príklade máme vo vnútri cyklu iba príkaz cout << " " << i;, ktorý vypíše medzeru a hodnotu premennej i.
  • Po skončení cyklu sa pokračuje príkazmi za končiacou zloženou zátvorkou, v našom prípade vypíšeme ešte koniec riadku.
  • Všimnite si, že náš program obsahuje dve sady zložených zátvoriek vnorených v sebe: jedna ohraničuje celý program, jedna ohraničuje príkazy, ktorá sa robia vo vnútri cyklu.
  • O presnom fungovaní príkazu for si povieme viac neskôr, ale teraz sa pozrime, čo spraví jednoduchá zmena v tomto príkaze:
for (int i = 1; i <= 10; i++) {
  • Počiatočnú príkaz sme zmenili z int i = 0 na int i = 1, premenná i teda začne s hodnotou 1, nie 0. Cyklus môže začať od ľubovoľnej hodnoty (napríklad aj zápornej)
  • Podmienku ukončenia sme zmenili z i < 10 na i <= 10, t.j. cyklus sa opakuje kým je hodnota premennej i menšia alebo rovná 10 (to isté by sme dosiahli aj pomocou i < 11)
  • Program teda vypíše čísla od 1 po 10:
 1 2 3 4 5 6 7 8 9 10

Vykreslenie náhodných kruhov

Nasledujúci program vykreslí na obrazovku náhodne rozmiestnené kruhy.

#include "../SVGdraw.h"
#include <cstdlib>
#include <ctime>

int main(void) {
    int size = 300; /* veľkosť obrázku */
    int count = 30; /* počet kruhov */
    int diameter = 10; /* priemer kruhu */

    /* inicializácia generátora pseudonáhodných čísel */
    srand(time(NULL));

    /* Vytvor obrázok s rozmermi 200x300 pixelov a 
     * ulož ho do súboru domcek1.svg*/
    SVGdraw drawing(size, size, "kruhy1.svg");
    
    for (int i = 0; i < count; i++) {
        /* vykresli kruh s priemerom diameter na náhodné miesto */
        drawing.drawEllipse(rand() % size, rand() % size, diameter, diameter);
    }
 
    /* Ukonči vypisovanie obrázka. */
    drawing.finish();
}
  • Program využíva príkaz rand(), ktorý generuje pseudonáhodné celé čísla. (Nie sú v skutočnosti náhodné, lebo ide o pevne definovanú matematickú postupnosť, ktorá však má mnohé vlastnosti náhodných čísel).
    • Výsledkom rand() je celé nezáporné číslo medzi 0 a nejakou veľkou konštantou.
    • rand() % size je číslo medzi 0 a size-1
    • vygenerujeme dve také čísla a použijeme ich ako súradnice kruhu (presnejšie ľavého horného rohu štvorca opísaného kruhu)
  • Príkaz srand inicializuje generátor pseudonáhodných čísel na určitú hodnotu, my použijeme aktuálny čas.
  • Potrebujeme knižnice cstdlib a ctime.

Skúsme pred príkaz drawing.drawEllipse dať ešte príkaz drawing.setFillColor, ktorý nastaví farbu výplne kruhu. Túto farbu chceme tiež nastaviť na náhodnú hodnotu, t.j. vygenerujeme tri náhodné čísla pre červenú, zelenú a modrú zložku tejto farby. Tieto čísla majú byť medzi 0 a 255.

/* nastav náhodnú farbu */
drawing.setFillColor(rand() % 256, rand() % 256, rand() % 256);

Cvičenie: čo by sa zmenilo, keby sme príkaz drawing.setFillColor dali pred for-cyklus?

Cvičenie: nastavme aj priemer kruhu ako náhodné číslo od 0 do 19. Čo ak chceme priemer od 10 do 19?

Organizačné poznámky

  • V piatok nebudú doplnkové cvičenia kvôli dekanskému voľnu (imatrikulácie)
  • Do utorka 22:00 odovzdať DÚ1
  • Budúci týždeň bežný rozvrh, vrátane oboch prednášok, cvičení a prvej rozcvičky
  • Test pre pokročilých:
    • Riadny termín budúci pondelok 26.9. 14:00
    • Náhradný termín ak nemôžete prísť na riadny utorok 14:50 (v čase cvičení)
    • Prihlásiť sa e-mailom B. Brejovej do piatku (uveďte ktorý termín)