Kurz C++ (3.)


Ve třetí lekci našeho kurzu o C/C++ se dostáváme k podstatné části jazyka. Nejdříve si povíme o funkcích getchar(), putchar(), printf() a scanf(). Povíme si o velice důležitém příkazu if-else. Dále si řekneme, co jsou to iterační příkazy neboli cykly a ukážu vám všechny typy takových cyklů. S cykly úzce souvisí příkazy break a continue a nakonec vysvětlím příkaz switch.

5.1. Terminálový vstup a výstup

Na začátek je nutné poznamenat, že vše, co je uvedeno v této lekci je podstata jazyka C a je nutné, abyste vše správně pochopili, protože při dalším programování se bez toho určitě neobejdete.

Abyste mohli využívat funkce pro vstup a výstup musíte k vašemu programu připojit hlavičkový soubor (header file). O vkládání hlavičkových souborů jsem se již zmínil v minulé lekci. Provede to příkazem include.

Vložte následující řádek na začátek vašeho programu:
  #include <stdio.h>

Nyní můžete používat všechny funkce deklarované právě v souboru stdio.h a které souvisí s I/O (input/output - vstup/výstup) operacemi jako je i výstup na obrazovku a vstup z klávesnice (dále může obsahovat například výstup na tiskárnu či čtení a zápis do souboru).

5.1.1. Vstup a výstup jednoho znaku

Výstup jednoho znaku zajišťuje funkce putchar(), která má jeden parametr kupodivu typu int (nikoliv char), který představuje hodnotu znaku v ASCII tabulce.

Druhá funkce getchar() přečte jeden znak. Pokud na řádek napíšeme víc znaků a stiskneme Enter, funkce přečte pouze první znak a ostatní ignoruje.

Program přečte znak, vypíše ho a odřádkuje.
Krátky příklad:

  int c;

  c = getchar();
  putchar(c);
  putchar('\n');

Všimněte si, že jako parametr funkce putchar() můžete použít znakovou konstantu tj. znak uzavřený mezi apostrofy např. 'a', '5' apod. Hodnota znakové konstanty odpovídá celočíselné hodnotě z ASCII tabulky např. 'a' má hodnotu 97. (ASCII tabulka nemusí být na všech systémech stejná).

Zápis
putchar(97);
a
putchar('a');
je ekvivalentní.

5.1.2. Formátovaný vstup a výstup

Co když ale chceme přečíst více než jeden znak nebo naopak chceme vypsat celou větu? Od toho má C dvě funkce:

  • scanf() - načte znaky, řetězce nebo čísla z klávesnice a uloží je v požadovaném formátu do proměnné.
  • printf() - vypíše řetězec v požadovaném formátu na obrazovku.
Způsob formátování
Obě funkce mají proměnný počet parametrů a my jim tedy musíme říct, kolik parametrů mají zpracovávat. K tomu slouží první parametr, což je řetězcová konstanta (nebo také řídící řetězec) obsahující jisté znaky, které právě říkají kolik má mít funkce parametrů a jakého jsou typu.

Řídící řetězec může obsahovat:
  • Formátové specifikace, které vždy začínají znakem "%" (procenta) a určují typ vstupní nebo výstupní hodnoty. Konkretní typ je určen znaky, které následuje těsně za tímto znakem.
  • Znakové posloupnosti, jsou běžné čitelné řetezce, které se vypíší tak jak jsou, ale dají se použít jen ve funkci printf().
Tabulka nejčastěji používaných formátových specifikací:
Znak za %Typ parametru
cznak - je lepší použít funkci getchar() nebo putchar()
dcelé číslo typu signed int
ldcelé číslo typu signed long
ucelé číslo typu unsigned int
lucelé číslo typu unsigned long
freálné číslo typu float
lfreálné číslo typu double
Lfreálné číslo typu long double (L musí být skutečné velké)
xčíslo v hexadecimálním tvaru s malými písmeny - 5b8f
Xčíslo v hexadecimálním tvaru s velkými písmeny - 5B8F
očíslo v osmičkovém tvaru
sřetězec

Příklady použití printf():
  1. printf("Je presne %2d:%2d\n", hodiny, minuty);
    Vypíše například "Je presne 21:27".
    Číslem mezi % a formátovým znakem je počet cifer, které se vypíší. U reálného čísla lze určit počet cifer před a za desetinou tečkou následovně:
  2. printf("Cena vyrobku je %6.2f Kc\n", cena);
    Vypíše například "Cena vyrobku je 10.40 Kc".
    Bude vytištěno 6 cifer z toho 2 budou za desetinou tečkou.
  3. printf("Cislo %d je hexa %XH\n", cislo, cislo);
    Vypíše například "Cislo 85 je v hexa 55H".
    Všimněte si konverze dekadického čísla na hexadecimální číslo.
Příklady použití scanf():
  1. scanf("%d", &cele_cislo);
    Tento příkaz přečte celé číslo z klávesnice a uloží ho do proměnné cele_cislo.
    Pozor! Všimněte znaku & před proměnnou cele_cislo. Funkci scanf() předáváme ukazatel na proměnnou cele_cislo a tento operátor vrací právě ukazatel. To je rozdíl od předchozí funkce a začínající programátor na to často zapomene. Kompilátor žádnou chybu nevypíše, ale program funguje chybně. Zatím vám stačí vědět, že na tento operátor nesmíte zapomenout.
  2. scanf("%f", &realne_cislo);
    Tento příkaz přečte reálné číslo z klávesnice a uloží ho do proměnné realne_cislo.

Poznámka: Minule jsem se zmiňoval o objektech cout a cin. Tyto objekty mají stejnou funkci jako funkce printf() a scanf().

5.2. Booleovské výrazy

Booleovský výraz je výraz, který vrací buď logickou 1 (TRUE - pravda) nebo logickou 0 (FALSE - nepravda). Jazyk C nemá přímo typ Boolean a tak místo toho využívá typ int, kde hodnota 0 představuje FALSE a nenulová hodnota (nejčastěji 1, ale není to podmínkou) představuje TRUE. Následující tabulka ukazuje tzv. relační operátory, které používáme v podmínkách. Vše si ukážeme na příkladu.
OperaceSyntaxe
rovnost= =
nerovnost!=
logický součin&&
logický součet||
negace!
větší>
menší<
větší nebo rovno>=
menší nebo rovno<=

Každý booleovský výraz obsahuje jeden nebo více těchto operátorů.

Příklad:
 // Toto je prirazeni, nikoliv porovnani, menime hodnotu promenne i
 i = 5

 // Zde vidite porovnani hodnoty promenne i a cisla 5
 // Vyraz vraci nenulovou hodnotu (TRUE) v pripade ze v promenne i je skutecne cislo 5, jinak vraci 0 (FALSE)

 i == 5  

5.1.1. Zkrácene vyhodnocování logických výrazů

Zkrácené vyhodnocování platí pro logické operace tzn. logický součin a logický součet. Znamená to, že jakmile je možno určit konečný výsledek vyhodnocování skončí.

Příklad:
 // Toto je zcela správné a k dělení nulou nikdy nedojde,
 // protože y != 0 ukončí vyhodnocování dříve než by k něčemu takovému došlo

 if(y != 0 && x / y < z)
 

5.2. Příkaz if a if-else

Jak jsme si říkali výše, booleovský výraz vrací někajou logickou hodnotu (0 nebo 1). K vyhodnocení takovéhoto výrazu použijeme příkaz if:

 if(výraz) { // zavorky jsou nutne
   příkaz_1;
 }
 příkaz_2;

Pokud je podmínka výrazu splněna (vrací TRUE), program provede příkaz_1 a poté i příkaz_2. V opačném případě (pokud výraz vrátí FALSE) je proveden pouze příkaz_2.

Příklad:

 // Program testuje zdali hodnota promenne i je vetsi nez 5
 // V kladnem pripade vypise hlasku
 if(i > 5) {
   printf("Hodnota promenne i je vetsi nez 5\n");
 }

Všimněte si, že výraz je vždy uzavřen do kulatých závorek () a že příkazy "pod" if jsou uzavřeny do závorek složených {}. Pokud je "pod" if pouze jeden příkaz, složené závorky nejsou nutné, naproti tomu kulaté jsou nutné vždy!

Příkaz if-else se liší jen málo. Příkazy "pod" else jsou prováděny jen když výraz vrací FALSE.
Česky řečeno:
"Když (if) něco (výraz), tak udělej tohle (příkaz_1), jinak (else) udělej tamhleto (příkaz_2)."


Příklad:

 // Program testuje zdali hodnota promenne i je vetsi nez 5
 // V obou pripadech vypise logickou hlasku
 if(i > 5) {
   printf("Hodnota promenne i je vetsi nez 5\n)";
 }
 else {
   printf("Hodnota promenne i je mensi, rovna nez 5\n)";
 }

Všimněte si, jak jsou příkazy ať už "pod" if nebo else, mírně odsazeny oproti ostatním příkazům. Je to zdůvodu čitelnosti programu a silně doporučuji se toho držet.
Příkazy if-else jsou často vhnízděné do jiných příkazů if nebo if-else. Každé další if je odsazeno o jeden tabelátor doprava.
Příklad:

 // Program nejprve testuje hodnotu promenne a a teprve pak pokracuje
 if(a != 0) {
   if(i > 5) {
     printf("Hodnota promenne i je vetsi nez 5\n");
   }
   else {
     printf("Hodnota promenne i je mensi nebo rovno nez 5\n");
   }
 }

Teď znovu přepíšu předchozí příklad, ale trochu jinak:

 // Zde vidite uzity operator logickeho soucinu neboli AND (zaroven)
 // Vyraz v zavorkach tedy znamena:
 // Pokud a je ruzne od 0 A ZAROVEN i je vetsi nez 5, pak vypis hlasku
 if(a != 0 && i > 5) {
   printf("Hodnota promenne i je vetsi nez 5\n");
 }

V následujím příkladu vidíte logický součet:

 // Zde vidite uzity operator logickeho soucinu neboli OR (nebo)
 // Vyraz v zavorkach tedy znamena:
 // Pokud a je ruzne od 0 NEBO i je vetsi nez 5, pak vypis hlasku
 if(a != 0 || i > 5) {
   printf("Hodnota promenne i je vetsi nez 5\n");
 }


Poznámka:
Často můžeme vidět příkaz if zkrácený.

Místo příkazu:
 if(vyraz != 0)
píšeme jen
 if(vyraz)

a místo příkazu:
 if(vyraz == 0)
píšeme jen
 if(!vyraz)

5.3. Ternární operátor

Ternární operátor má stejný význam jako příkaz if-else, jen syntaxe je jiná. V některých případech je lepší použít ternární operátor kvůli kratšímu zápisu na jeden řádek, ale doporučuji raději používat if-else.

Ternární operátor má následující syntaxy:

 vyraz_podm ? vyraz_1 : vyraz_2;

Příklad:

 // Pokud je a ruzne od 0, i bude rovno 5
 // V opacnem pripade bude i rovno 10
 i = (a != 0) ? 5 : 10;

Stejný příklad s použitím if-else:

 // Funkce je uplna stejne jako v predchozim prikladu
 // Vidite ze zapis pomoci if-else je delsi, ale je prehlednejsi
 if(a != 0) {
   i = 5;
 }
 else {
   i = 10;
 }


Poznámka: Závorky kolem podmínky u ternárního operátoru nejsou nutné, ale vřele je doporučuji kvůli čitelnosti programu.

5.4. Iterační příkazy - cykly

Jazyk C rozlišuje tři typy iterace (opakování): for, while a do-while.

5.4.1. Příkaz for

Tento cyklus použijeme v případě, když předem známe počet průchodů cyklem.
Syntaxe je následující:

 for(vyraz_start; vyraz_stop; vyraz_iter) {
    prikaz;
 }

Cyklus for nejdříve vyhodnotí vyraz_start, otestuje pravdivost vyrazu_stop (když výraz vratí FALSE, cyklus skončí) a provede příkazy uvnitř cyklu a nakonec provede vyraz_iter. Toto celé se opakuje dokud vyraz_stop nevratí nepravdu (FALSE).

Ukážeme si několik příkladů:

// Program vypise pod sebe 20 cisel (0-19)
// Promenna i je predem deklarovana jako int
// Toto je doporucene pouziti cyklu for

 for(i = 0; i < 20; i++) {
    printf("%d\n", i);
 }

// Program vypise pod sebe 20 cisel (19-0)
// Promenna i je deklarovana primo v cyklu (toto nemusi vzdy fungovat)
// Toto je caste pouziti cyklu for

 for(int i = 19; i >= 0; i--) {
    printf("%d\n", i);
 }

Můžeme využít nekonečný cyklus:

// Takovy program radeji nezkousejte, protoze nikdy neskonci
// ale za chvilku si povime jak ukoncit cyklus explicitne
 for( ; ; ) {
    printf("Ahoj\n");
 }

5.4.2. Příkaz while

Dalším velice důležitým cyklem je cyklus while. Testuje podmínku před průchodem cyklu, tzn., že cyklus nemusí proběhnout ani jednou. Ukončovací podmínka většinou závisí na příkazu v těle cyklu a my předem nevíme kolikrát cyklus proběhne.

Syntaxe:

 while(podm) {
    prikaz;
 }

Výraz podm se testuje a pokud vrátí 0 (FALSE), cyklus skončí.

5.4.3. Příkaz do-while

Posledním iteračním cyklem je do-while (dělej-dokud). Liší se od předchozáho tím, že testuje podmínku až po průchodu cyklem tzn., že cyklus proběhne alespoň jednou. Cyklus je ukončen až když výraz v podmínce vrátí 0 (FALSE).
Syntaxe:

 do {
    prikaz;
 } while(podm);

5.4.4. Příkazy break a continue

Příkazy break a continue ovlivňují normální průběh cyklu. Lze je použít na všechny výše uvedené typy cyklů.

  • break
    Tento příkaz ukončuje nejvnitřnější smyčku neboli opouští okamžitě cyklus.
  • continue
    Druhý uvedený příkaz skáče na konec cyklu a tím si vynutí další opakování cyklu, ale cyklus neopouští.

5.4.5. Příkaz switch

C obsahuje jakýsi přepínač neboli switch, který umožňuje mnohonásobné větvení programu.

Syntaxe:

 switch(vyraz) {
 case hodnota_1:   // Toto je jedna vetev
    prikaz_1;      // ...
    break;         // ...
 case hodnota_2:   // A zde zacina dalsi vetev
    prikaz_2;
    break;
 case hodnota_3:
    prikaz_3;
    break;
 default:
    prikaz_def;
    break;
 }

Program porovnává vyraz s jednotlivými hodnotami_X každé větve a provede tu větev, kde se vyraz rovná hodnote_X.


Pozor! Není-li větev ukončena příkazem break, program začne zpracovávat další větve v pořadí dokud nenarazí na break. Proto je třeba na konci každé větve psát break. Z tohoto ale vyplývá, že pokud chceme pro více hodnot zpracovat pouze jednu větev, stačí vynechat příkaz break v těchto větvích.
Například:

 switch(vyraz) {
 case hodnota_1:
    prikaz_1;
    break;
 case hodnota_2:// Zde neni break
 case hodnota_3:
    prikaz_23;
    break;
 }

Prikaz_23 se provede, když je vyraz roven buď hodnote_2 nebo hodnote_3.

5.5. Příklad

Vše si samozřejmě ukážeme na příkladu. Příklad si můžete stáhnout z CD v sekci Downloads. Vše, co potřebujete vědět k příkladu je v této nebo v minulé lekci a popis funkce najdete v komentářích.

  #include <stdio.h>

  int main(int argc, char* argv[])
  {
    int iVolba, i;
    char c;
    double f, g;

    do {
      //
      // Informace pro uzivatele
      printf("1) Test smycky for\n");
      printf("2) Test smycky while\n");
      printf("3) Test smycky do-while \n");
      printf("4) Konec programu\n");
      printf("Zadejte co chcete otestovat:");
      //
      // Ulozime uzivatelovu volbu do promenne iVolba jako hodnotu typu int
      scanf("%d", &iVolba);
      //
      // Vetveni programu podle toho, co zvoli uzivatel
      switch(iVolba) {
      case 1:
        //
        // Test cyklu for
        printf("Zvolil jste prikaz for, vypisu 10 cisel a skoncim.\n");
        //
        // Cyklus for od 0 do 9
        for(i = 0; i < 10; i++) {
          printf("%d\n", i);
        }
        //
        // Nakonci jeste odradkujeme
        printf("\n");
        break;
      case 2:
        printf("\nZvolil jste prikaz while.\n");
        printf("Program cte znaky z klavesnice, tisknutelne znaky opisuje,\n");
        printf("neviditelne preskakuje a zastavi se pri precteni znaku 'z'.\n\n");
        //
        // Precteni znaku
        while((c = getchar()) != 'z') {
          if(c >= ' ') { // Vynechavame neviditelne znaky
            putchar(c); // Tisk znaku
            printf("\n"); // Odradkuju
          }
        }
        break;
      case 3:
        printf("Zvolil jste prikaz do-while. \n");
        printf("Program nacte dolni a horni mez intervalu\n");
        printf("a pote vypise vsechny cela cisla z tohoto intervalu.\n");
        //
        // Nacteme dve realna cisla
        printf("Zadejte dolni mez intervalu:");
        scanf("%lf", &f);
        printf("Zadejte horni mez intervalu:");
        scanf("%lf", &g);
        //
        // Test zdali je horni mez skutecne vetsi nez dolni mez
        if(f < g) {
          //
          // Zaokrouhleni realneho cislo na cele cislo (explicitni konverze)
          i = (int) f;
          do {
            // Vypsani celeho cisla
            printf("%d\n", i);
            // Prejdeme na dalsi cele cislo
            i++;
            // A testujeme zdali jsme jiz dosahli horni hranice intervalu
          } while(i <= g);
        }
        else {
          printf("Chyba! Zadali jste chybne meze.\n");
        }

        break;
      case 4:
        //
        // Konec
        printf("Konec...\n");
        break;
      default:
        printf("Zvolil jste kravinu\n");
        break;
      }
    //
    // Testujeme promennou iVolba na hodnotu 4,
    // coz je volba, pri ktere ma program skoncit
    } while(iVolba != 4);

    return 0;
  }


Poznámka: Opět si všimněte odsazení jednotlivých větví příkazu switch a odsazení těl cyklů. Toto je velmi dobré dělat kvůli přehlednosti v programu.

5.6. Závěr

Na závěr vám prozradím, co nás čeká příště. Příště probereme preprocesor jazyka a funkce. Mezi příkazy preprocesoru patří i nám známý příkaz include. Zjistíte, že před každým příkazem preprocesoru je znak '#'. Dále si vytvoříme vlastní funkce mimo funkci main(). Tyto funkce pak budeme volat z funkce main().

Těším se příště nashledanou.