Oznamy

Najbližšie dni na programovaní

  • Dnes prednáška, ďalšie dve prednášky budúci týždeň
  • Do pondelka 22:00 riešte úlohy zo včerajšieho cvičenia, ktoré sú na testovači.
  • Ideálne ich zvládnete sami, ale ak budete mať problémy, spýtajte sa na cvičení budúci pondelok.
  • Ak vám testovač k príkladu vypíše zelené OK, prešiel testami. Ak vypíše oranžový kód chyby, niečo je zle, treba opraviť a odovzdať znovu.
  • Budúci pondelok aj v utorok sa na začiatku cvičenia objavia na testovači nové úlohy.
  • Neskôr v semestri budú na cvičeniach aj iné aktivity, napr. riešenie úloh na papieri.
  • Dnes o 18:10 nepovinný test pre pokročilých v posluchárni A (bolo sa treba prihlásiť do včera).

Technické záležitosti

  • Na stránke Softvér nájdete návod na používanie softvéru v učebni a aj príklady programov, ktoré si môžete nainštalovať na vlastný počítač.
  • Ak máte problémy s inštaláciou, prineste si svoj počítač na cvičenie a skúsime vám poradiť, pokiaľ zostane čas. Váš počítač je ale hlavne vaša zodpovednosť.

Ďalšie upozornenia k štúdiu:

  • V rozvrhu https://candle.fmph.uniba.sk/ vidíte všetky predmety, ktoré sú povinné, povinne voliteľné alebo výberové pre váš ročník, ale nie všetky si musíte zapísať a naopak, môžete si zapísať aj iné predmety.
  • Dôležité je skontrolovať si, či to máte zapísané v AIS, sedí s tým, na čo aj reálne chodíte.
  • Prvé dva týždne (do 2.10.) si môžete v AIS pridávať a uberať predmety, potom treba kontaktovať študijné emailom, aby vám uzatvorili zápisný list.
  • Prvácke povinné predmety by ste si mali zapísať, z výberových si môžete vybrať.
  • Pozor na to, aby ste získali aspoň 15 kreditov za zimný semester.

Opakovanie

Doteraz sme videli:

  • Načítavanie pomocou scanf, výpis pomocou printf.
  • Celočíselné premenné typu int.
#include <stdio.h>

int main() {
    int x, y;

    printf("Please enter the first number: ");
    scanf("%d", &x);
    printf("Please enter the second number: ");
    scanf("%d", &y);

    int result = x + y;
    printf("%d + %d = %d\n", x, y, result);
}

Komentáre

Do zdrojových kódov programov v jazyku C je možné pridávať komentáre, čo sú časti kódu ignorované kompilátorom.

  • Komentár je časť programu začínajúca /* a končiaca */ (aj cez viac riadkov)
  • Komentár je aj text od // až po koniec riadku. To je užitočné na písanie krátkych komentárov.

Do komentárov sa zvyknú písať poznámky k významu okolitých príkazov, čo zlepšuje orientáciu v kóde a jeho pochopenie inými programátormi.

Príklad:

#include <stdio.h>

/* Tento program od používateľa načíta dve celé čísla
 * a vypíše ich súčet. */

int main() {
    // vytvoríme premenné x a y
    int x, y;

    // do premenných od používateľa načítame čísla
    printf("Please enter the first number: ");
    scanf("%d", &x);
    printf("Please enter the second number: ");
    scanf("%d", &y);

    // do novej premennej result spočítame výsledok
    int result = x + y;
    // výsledok vypíšeme
    printf("%d + %d = %d\n", x, y, result);
}

Podmienka (if)

  • Niekedy chceme vykonať určité príkazy len ak sú splnené nejaké podmienky. To nám umožňuje príkaz if.
  • Nasledujúci program si vypýta od používateľa číslo a vypíše, či je toto číslo záporné alebo nezáporné.
#include <stdio.h>

int main() {
    int x;
    printf("Zadajte cislo: ");
    scanf("%d", &x);

    if (x < 0) {
        printf("Cislo %d je zaporne.\n", x);
    } else {
        printf("Cislo %d je nezaporne.\n", x);
    }
}

Tu je príklad dvoch behov programu:

Zadajte cislo: 10
Cislo 10 je nezaporne.

Zadajte cislo: -3
Cislo -3 je zaporne.
  • Za príkazom if je zátvorka s podmienkou. V našom príklade podmienka je x < 0.
  • Ak je podmienka v zátvorke splnená (t.j. ak x je menšie ako nula), vykonáme príkazy v zloženej zátvorke za príkazom if.
  • Ak podmienka nie je splnená (t.j. ak je x väčšie alebo 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.
  • Ak za if alebo else nasleduje iba jeden príkaz, zátvorky { a } môžeme vynechať. To však ľahko vedie k chybám, preto je lepšie ich vždy použiť.

Cvičenia:

  • Pomocou podmienky vypíšte absolútnu hodnotu načítaného čísla.
  • Namiesto vypísania uložte túto hodnotu do premennej y, ktorá by sa dala ďalej v programe použiť.

Operátory

S číselnými premennými potrebujeme robiť rôzne operácie, napr. sčítanie, odčítanie, násobenie a delenie, porovnávanie.

Aritmetické operátory a výrazy pre celé čísla

  • sčítanie +
  • odčítanie -
  • násobenie *
  • delenie / pre celé čísla sa používa celočíselné delenie, hodnota podielu zaokrúhli smerom k nule
  • zvyšok po celočíselnom delení % (modulo)

Tento program načíta dve celé čísla a vypíše výsledky rôznych operácií:

#include <stdio.h>

int main() {
    int a, b;
    printf("Zadajte dve cele cisla: ");
    scanf("%d %d", &a, &b);
    printf("%d + %d = %d\n", a, b, a + b);
    printf("%d - %d = %d\n", a, b, a - b);
    printf("%d * %d = %d\n", a, b, a * b);
    printf("%d / %d = %d\n", a, b, a / b);
    printf("%d %% %d = %d\n", a, b, a % b);
}

Všimnite si, že pri vypísaní zvyšku po delení sme použili %%, aby sa vypísal znak %. V opačnom prípade by príkaz printf očakával za znakom % typ premennej, ktorý sa má vypísať, napríklad %d.

Príklad behov programu:

Zadajte dve cele cisla: 23 10
23 + 10 = 33
23 - 10 = 13
23 * 10 = 230
23 / 10 = 2
23 % 10 = 3
Zadajte dve cele cisla: -23 10
-23 + 10 = -13
-23 - 10 = -33
-23 * 10 = -230
-23 / 10 = -2
-23 % 10 = -3

Zadajte dve cele cisla: 23 0
23 + 0 = 23
23 - 0 = 23
23 * 0 = 0
Floating point exception (core dumped)

Všimnite si celočíselné delenie: 23/10 bolo 2 a -23/10 bolo -2.

V poslednom behu nastala chyba kvôli deleniu nulou. Program sa predčasne ukončil a nevypísal posledné dva riadky.

Relačné operátory

Celé čísla možno porovnávať nasledujúcimi relačnými operátormi:

  • == pre rovnosť;
  • != pre nerovnosť;
  • < pre reláciu „menší”;
  • > pre reláciu „väčší”;
  • <= pre reláciu „menší alebo rovný”;
  • >= pre reláciu „väčší alebo rovný”.

Výstupom relačného operátora je logická hodnota true (pravda, 1) alebo false (nepravda, 0).

Logické operátory

Logické hodnoty true (1) alebo false (0) možno kombinovať pomocou logických operátorov. ktoré sa správajú ako logické spojky z výrokovej logiky:

  • || pre disjunkciu (or, alebo);
  • && pre konjunkciu (and, a súčasne);
  • ! pre negáciu (not, opak)

Takže x || y je pravdivé, ak je aspoň jedna z hodnôt x alebo y pravdivá, x && y je pravdivé, ak sú obe hodnoty pravdivé a !x je pravdivé, ak je hodnota x nepravdivá.

Cvičenie: pre aké hodnoty premennej x je výraz !((x >= 2) && (x <= 4)) pravdivý?

Operátory priradenia, zvýšenie a zníženie hodnoty o 1

Operátor priradenia premenna = hodnota už poznáme. Často potrebnou operáciou na číslach je zvýšenie hodnoty o 1. To možno urobiť napríklad nasledujúcimi spôsobmi:

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

Analogicky sú definované operátory ako --, -=, *=, atď.

Dva programy s podmienkou

Vnorené podmienky

Príkazy if môžeme navzájom vnárať.

Príklad: načítaj číslo a zisti, či je kladné, záporné alebo nula.

#include <stdio.h>

int main() {
    int x;
    printf("Please enter some number: ");
    scanf("%d", &x);

    if (x == 0) {
        printf("Zero\n");
    } else {
        if (x > 0) {
            printf("Positive\n");
        } else {
            printf("Negative\n");
        }
    }
}

Počet núl

Príklad: načítaj tri čísla a vypíš, koľko z nich je nulových.

  • Vytvoríme premennú count, ktorá bude počítať počet núl. Postupne porovnáme každé číslo s nulou a ak je rovné nule, zvýšime count o 1. Na konci vypíšeme hodnotu count.
#include <stdio.h>
int main() {
    int a, b, c;
    printf("Zadajte tri cisla: ");
    scanf("%d %d %d", &a, &b, &c);

    int count = 0;
    if (a == 0) {
        count++;
    }
    if (b == 0) {
        count++;
    }
    if (c == 0) {
        count++;
    }

    printf("Pocet nul: %d\n", count);
}

Upozornenie

Častou chybou je použitie priradenia namiesto porovnania. Nasledujúci kúsok programu do premennej x priradí nulu, ktorá sa považuje za false pre účely vyhodnotenia podmienky.

if (x = 0) printf("zero\n");

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

if (x == 0) printf("x is zero\n"); count++;

Tento program vykoná count++ 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) { printf("x is zero\n"); count++; }

Cvičenia

  • Napíšte program, ktorý načíta čísla a,b,c a vypíše, či sú usporiadané vzostupne, t.j. či platí a<b<c
    • Pozor, výraz a<b<c treba rozpísať na dve porovnania spojené logickou spojkou

Dátový typ double pre reálne čísla

  • Doteraz sme pracovali s typom int pre celé čísla.
  • Často však potrebujeme pracovať s reálnymi číslami, ktoré môžu mať aj desatinnú časť. Na to slúži typ double.
  • V programe zapisujeme konštanty typu double s desatinnou bodkou, napr. -1.0 alebo 3.14
  • Môžeme použiť aj tzv. semilogaritmický zápis, napr. 1.5e3 (čo znamená 1,5 ⋅ 103 = 1500).
  • Operátory +, -, *, / sú definované aj pre typ double. Operátor / sa správa ako delenie reálnych čísel, t.j. výsledok sa nezaokrúhľuje na celé číslo. Operátor % nie je definovaný pre typ double.
double x = 1.5e3; // x = 1500.0
double y = 3.14;
double z = x / y; // z = 477.707006

Vypisovanie a načítavanie hodnôt typu double

Pri použití printf sa pre typ double používa formátovací reťazec %f a v scanf sa používa %lf.

Nasledujúci program načíta dve reálne čísla a vypíše výsledky rôznych operácií:

#include <stdio.h>

int main() {
    double a, b;
    printf("Zadajte dve realne cisla: ");
    scanf("%lf %lf", &a, &b);
    printf("%f + %f = %f\n", a, b, a + b);
    printf("%f - %f = %f\n", a, b, a - b);
    printf("%f * %f = %f\n", a, b, a * b);
    printf("%f / %f = %f\n", a, b, a / b);
}

Príklad behu programu:

Zadajte dve realne cisla: 2.3 10
2.300000 + 10.000000 = 12.300000
2.300000 - 10.000000 = -7.700000
2.300000 * 10.000000 = 23.000000
2.300000 / 10.000000 = 0.230000

Dátový typ bool pre logické hodnoty

  • Logické hodnoty môžeme reprezentovať pomocou typu bool
  • V jazyku C je potrebné ho definovať pomocou knižnice stdbool.h.
  • Premenné typu bool môžu nadobúdať iba dve hodnoty: true (pravda, 1) a false (nepravda, 0).
  • Vypisujeme ich ako celé čísla pomocou %d, načítavať ich priamo nebudeme.
#include <stdio.h>
#include <stdbool.h>

int main() {
    // vytvorenie logických premenných
    bool b1 = true;
    bool b2 = false;
    bool b3 = (1 < 2) || b2; // b3 bude true

    // výpis logických hodnôt ako celých čísel
    printf("b1 = %d, b2 = %d, b3 = %d\n", b1, b2, b3);

    // použitie logických hodnôt v podmienke
    if (b3) {
        printf("b3 je pravda\n");
    } else {
        printf("b3 je nepravda\n");
    }
}

Program vypíše:

b1 = 1, b2 = 0, b3 = 1
b3 je pravda

Cyklus for

Dôležitou časťou programovania je schopnosť opakovanie vykonávať tie isté príkazy. Prvou možnosťou ako to robiť, je cyklus for.

Príklad 1: vypisovanie čísel od 1 po n

Nasledujúci program načíta zo vstupu číslo n a postupne vypíše prirodzené čísla od 1 po n (pred každé dá medzeru).

#include <stdio.h>

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        printf(" %d", i);
    }
    printf("\n");
}

Tu je výstup programu pre n = 9:

 1 2 3 4 5 6 7 8 9

Cyklus for vyzeral v programe takto:

for (int i = 1; i <= n; i++) {
    telo cyklu
}

Táto konštrukcia pozostáva z kľúčového slova for nasledovaným zátvorkou s troma časťami oddelenými bodkočiarkami:

  • Príkaz int i = 1 vytvorí novú celočíselnú premennú i a priradí jej hodnotu 1.
  • Podmienka i <= n určuje dokedy sa má cyklus opakovať. V tomto prípade to má byť kým je hodnota premennej i menšia alebo rovná n.
  • Príkaz i++ hovorí, že po každom zopakovaní cyklu (t.j. po každej jeho iterácii) sa má hodnota premennej i zvýšiť o jedna.
  • Medzi zloženými zátvorkami { a } je potom tzv. telo cyklu – čiže jeden alebo viac príkazov, ktoré sa budú opakovať postupne pre rôzne hodnoty premennej i.
    • V príklade 1 je telom cyklu iba príkaz printf(" %d", i);, ktorý vypíše medzeru a hodnotu premennej i.

Čo sa stane ak zadáme n = 0?

Príklad 2: vypisovanie čísel od 0 po n-1

Drobnou zmenou predchádzajúceho programu môžeme napríklad vypísať všetky čísla od 0 po n-1:

#include <stdio.h>

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        printf(" %d", i);
    }
    printf("\n");
}

Tu je výstup programu pre n = 9:

 0 1 2 3 4 5 6 7 8

Ďalšie detaily o typoch int a double

Rozsah hodnôt

Premenné typu int a double zaberajú pevne daný počet bitov, preto do nich nie je možné uložiť ľubovoľné celé alebo reálne číslo. Presný rozsah možných hodnôt môže závisieť od kompilátora, v súčasnosti však väčšinou platí:

  • Typ int zvyčajne zaberá 4 bajty (32 bitov) a dajú sa ním reprezentovať celé čísla z intervalu <-2 147 483 648, +2 147 483 647>.
  • Typ double zvyčajne zaberá 8 bajtov. Ním reprezentované reálne čísla sú v pamäti uložené vo forme z ⋅ a ⋅ 2b, 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. Typ double tak možno použiť na prácu s reálnymi číslami približne v rozsahu od 10-300 po 10300 s presnosťou na 15 až 16 platných číslic. Pri tejto reprezentácii sa nevyhradzujú pevné počty bitov na reprezentáciu celej a desatinnej časti; počet cifier pred a za rádovou čiarkou je určený exponentom. Hovoríme preto o pohyblivej rádovej čiarke.

Pretypovanie

Hodnotu niektorého z typov bool, int, double je možné skonvertovať na „zodpovedajúcu” hodnotu iného z týchto typov, tejto operácii hovoríme pretypovanie.

  • Pretypovanie int na double: hodnota čísla zostáva tá istá
  • Pretypovanie double na int: zaokrúhleniu smerom k nule (čiže nadol pri kladných číslach, nahor pri záporných), t.j. “urezanie” desatinných cifier
  • Pretypovanie bool na int alebo double: true sa konvertuje na 1 alebo 1.0 a false na 0 alebo 0.0
  • Pretypovanie z int alebo double na bool: nula sa skonvertuje na false, ľubovoľná nenulová hodnota na true

Pretypovanie je možné realizovať dvoma spôsobmi:

  • Implicitne, napríklad priradením premennej jedného typu do premennej iného typu, alebo
  • Explicitne, použitím pretypovacieho operátora: (nazov_noveho_typu) vyraz_stareho_typu.

Možnosti pretypovania sú ilustrované nasledujúcim ukážkovým programom.

#include <stdio.h>
#include <stdbool.h>

int main() {
  int n1 = 4;
  double x1 = 1.234;

  int n2 = x1; 
  printf("%d\n", n2); // Vypíše 1
  
  x1 = n2; 
  printf("%f\n", x1); // Vypíše 1.000000
  
  printf("%d %d\n", (int) 4.2, (int) -4.2); // Vypíše 4 -4
  
  bool b1 = n1;
  printf("%d %d\n", n1, (int)b1); // Vypíše 4 1
}

Celočíselné a reálne delenie

Operátor / sa na argumentoch typu int správa ako celočíselné delenie – hodnota podielu sa zaokrúhli smerom k nule.

  • Napríklad výraz 5/3 má hodnotu 1.
  • Akonáhle je však aspoň jeden operand typu double, interpretuje sa / ako delenie reálnych čísel.
  • Výrazy 5.0 / 3.0, 5.0 / 3, 5 / 3.0 a 5 / (double)3 teda majú všetky hodnotu 1.666667.

Nasledujúci program demonštruje implicitné a explicitné pretypovanie pri delení, t.j. kedy delíme celočíselne a kedy je výsledkom reálne číslo aj s desatinnými ciframi.

#include <stdio.h>

int main() {
    int a = 4;
    int b = 3;

    // Automaticky pretypuje cele cislo 3 na realne cislo 3.0
    double d = 3; 

    // Celociselne delenie: 4 / 3 = 1
    printf("%d\n", a / b); 
    // Necelociselne delenie: 4 / 3.0 = 1.333333
    printf("%f\n", a / d); 
    // Necelociselne delenie: (1.0 * 4) / 3 = 4.0 / 3 = 1.333333
    printf("%f\n", (1.0 * a) / b); 
    // Necelociselne delenie: 4.0 / 3 = 1.333333 
    printf("%f\n", ((double)a) / b); 
    
    // Do e je priradeny vysledok celociselneho delenia 4 / 3 = 1; 
    // po pretypovani je to rovne 1.0
    double e = a / b; 
    // Vypise 1.000000
    printf("%f\n", e); 
    // Vypise 0.500000, lebo 1.0 / 2 je necelociselne delenie  
    printf("%f\n", e / 2); 
}

Priorita a asociativita operátorov

Výrazy sa vyhodnocujú v nasledujúcom poradí preferencie jednotlivých operátorov. Operátory v jednom riadku majú rovnakú prioritu a operátory vo vyššom riadku majú vyššiu prioritu, než operátory v nižších riadkoch.

  • ++ (inkrement), -- (dekrement), ! (logická negácia)
  • *, /, %
  • +, -
  • <, >, <=, >=
  • ==, !=
  • && (logická konjunkcia)
  • || (logická disjunkcia)
  • = (priradenie)

Poradie vyhodnocovania je možné meniť zátvorkami, ako napríklad vo výraze 4*(5-3).

Uvedené operátory sa väčšinou vyhodnocujú zľava doprava (hovoríme, že sú zľava asociatívne) – napríklad 1 - 2 - 3 sa teda vyhodnotí ako (1 - 2) - 3, t.j. -4 a nie ako 1 - (2 - 3), t.j. 2. Výnimkou sú operátory !, ++, -- a =, ktoré sú sprava asociatívne. To umožňuje napríklad viacnásobné priradenie a = b = c, ktoré najprv priradí hodnotu c do b a následne hodnotu výrazu b = c – tou je nová hodnota premennej b, čiže hodnota premennej c –, do a.

Viac sa o operátoroch v C++ možno dočítať napríklad tu.

Zhrnutie

Poznáme základné stavebné prvky, z ktorých sa dajú spraviť aj pomerne zložité programy:

  • premenné typu int, double, bool a operátory, ktoré s nimi vedia počítať
  • podmienku if, ktorá umožňuje vykonávať určité časti programu len za nejakých okolností
  • cyklus for, ktorý umožnuje opakovať určité časti programu veľa krát

Cieľom najbližšej prednášky a cvičení je hlavne precvičiť si tieto stavebné prvky na veľa ďalších príkladoch.