Podmínky a cykly

Konečně se dostáváme k té zábavné části programování. Konstrukce které v této kapitole vysvětlím dělají program programem. Díky nim se může program na základě dat rozhodovat, co bude dělat a tím takříkajíc „ožít“. Cykly vám zase umožní dělat nějaké akce opakovaně a tak výrazně zefektivní práci při programování. V této kapitole naplno využijete relačních operátorů.

Podmínka if-else

Podmínka if-else je velice podobná podmíněnému operátoru ? : a ještě více podmíněnému překladu preprocesoru.
Větvení programu pomocí podmínky if-else je následující:

if (podminka)
     telo bloku
else if (podminka)
     telo bloku
else if (podminka)
     telo bloku
else
     telo bloku

Podmínky musí být v kulatých závorkách. Tělo bloku může být blok (příkazy uzavřené v složených závorkách), nebo jen jeden příkaz (ukončený středníkem).
Větví else if může být konečně mnoho a nejsou povinné, stejně jako není povinná větev else.

Podmínky se vyhodnocují jedna za druhou do té doby, než se narazí na první pravdivou (TRUE). Pak se provedou příkazy v bezprostředně následujícím bloku. Další podmínky se již nevyhodnocují. Pokud se žádná podmínka nevyhodnotí jako TRUE, pak se provede tělo bloku za else (pokud část else existuje).

Else se umisťuje vždy na konec. Je to de facto to samé, jako byste uvedli na konci else if(TRUE) …

V příkladu ukáži několikeré využití podmínky if-else. Připomínám, že funkce scanf() má jako návratovou hodnotu počet správně načtených položek nebo EOF při pokusu číst ze zavřeného vstupu.

/*------------------------------------------------*/
/* c12/ifelse.c                                   */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

#define CISLO 5
#define MIN 0
#define MAX 10

int main(void)
{
    int x = -1;
    int navrat;
    printf("Na jake cislo myslim? Hadej mezi %2i a %2i: ", MIN, MAX);

    navrat = scanf("%i", &x);

    if (navrat == EOF) {
        printf("\ascanf nemuze cist. Je uzavren standardni vstup stdin\n");
        return 1;               /* ukoncime funkci main -> konec programu */
    } else if (navrat != 1) {   /* chceme nacist 1 polozku */
        printf("\aNezadal jsi cislo!\n");
        return 1;
    }

    if (x < MIN)
        printf("Tak to bylo malo trochu hochu!\n");
    else if (x > MAX)
        printf("Tak to bylo trochu moc!\n");
    else if (x == CISLO)
        printf("UHADNULS :-))\n");
    else
        printf("Smula. Zkus to znovu.\n");

    return 0;
}

 /*------------------------------------------------*/
Visual Studio

Makro _CRT_SECURE_NO_WARNINGS je tu kvůli funkci scanf(), viz scanf().

Možný výstup:

Na jake cislo myslim? Hadej mezi  0 a 10: 5
UHADNULS :-))

Zkuste si zadat místo čísla řetězec nebo uzavřít standardní vstup (CTRL+z v DOSu a CTLR+d v Linuxu).

Přepínač switch

Přepínač switch obsahuje několik návěstí (case) s celočíselnou hodnotou, nebo rozmezí NIZSI ... VYSSI (např. 1 ... 5 nebo 'a' ... 'e'). Použití intervalu NIZSI ... VYSSI není dle standardu jazyka C, ale rozšíření GNU překladače (gcc), takže vám s jiným překladačem pravděpodobně fungovat nebude (nefunguje např. s Visual Studiem).
Mezi třemi tečkami a hodnotami NIZSI a VYSSI vždy nechávejte mezery.

Na základě argumentu za klíčovým slovem switch přeskočí program na návěstí se stejnou hodnotou jakou má výraz v kulatých závorkách za switch a pokračuje vykonáváním příkazů za ním.
Přepínač switch může obsahovat návěstí default, na které program skočí tehdy, když argument za klíčovým slovem switch neodpovídá hodnotě za žádným návěstím case. Default není povinné a může se použít kdekoliv (první před všemi case, mezi nimi i jako poslední).

switch (vyraz)
{
  case hodnota:
        prikazy
  case hodnota:
        prikazy
  default:
        prikazy
}

Příkaz break je speciální příkaz, který způsobí „vyskočení“ z těla příkazu switch (a také cyklů, viz dále). Program pak pokračuje ve vykonávání příkazů za blokem switch.

V příkladu je použita funkce getchar() z knihovny <stdio.h>, která vrací hodnotu datového typu int. Proto jsem jí přetypoval na typ char.
Tato funkce načte ze standardního vstupu znak a vrátí jej jako svou návratovou hodnotu, přičemž vrací speciální hodnotu EOF v případě uzavřeného standardního vstupu.
Hodnota EOF je typu int, proto i getchar() vrací int, ale tuto eventualitu v programu nekontroluju. Vy již jistě víte, jak byste to (pomocí konstrukce if-else) udělali. Tuto a další podobné funkce budu probírat podrobněji později.

  1. /*------------------------------------------------*/
  2. /* c12/switch.c                                   */
  3. #include <stdio.h>
  4.  
  5. int main(void)
  6. {
  7.     char znak;
  8.  
  9.     printf("Opravdu chcete smazat vsechny data na disku? [a/n/k]> ");
  10.     znak = (char) getchar();
  11.  
  12.     switch (znak) {
  13.     default:
  14.         printf("Mel si zmacknout \'a\',\'n\' nebo \'k\' a ne \'%c\'\n", znak);
  15.     case 'k':
  16.         printf("Jako by nekdo zmacknul k\n");
  17.         return 0;
  18.     case 'N':
  19.         printf("Stejne smazu co muzu!\n");
  20.         break;
  21.     case 'n':
  22.         printf("Nechcete? Smula!\n");
  23.     case 'a':
  24.     case 'A':
  25.         printf("Data byla smazana !!!\n");
  26.         break;
  27. #ifdef __unix__
  28.      case 'b' ... 'e':
  29.         printf("Trefa do intervalu <b,e>: %c!\n", znak);
  30.         break;
  31. #endif
  32.     }
  33.     printf("Ne, nebojte, to byl jenom zertik.\n");
  34.     return 0;
  35. }
  36.  
  37. /*------------------------------------------------*/

Možný výstup:

 Opravdu chcete smazat vsechny data na disku? [a/n/k]> n
 Nechcete? Smula!
 Data byla smazana !!!
 Ne, nebojte, to byl jenom zertik.

nebo

 Opravdu chcete smazat vsechny data na disku? [a/n/k]> a
 Data byla smazana !!!
 Ne, nebojte, to byl jenom zertik.

nebo

Opravdu chcete smazat vsechny data na disku? [a/n/k]> x
Mel si zmacknout 'a','n' nebo 'k' a ne 'x'
Jako by nekdo zmacknul k

Všimněte si rozdílu mezi break a return 0. Příkaz break skočí za blok příkazu switch, ale return ukončí funkci main a tím celý program.

Poslední použití break (na konci switch) je zbytečné, protože switch už stejně končí. Je tam jen pro ilustraci.

Skok goto

Předem vás upozorňuji, že použití skoku goto může velice znepřehlednit program a jeho čtení se pak může stát hororem. Proto se vyhýbejte tomuto skoku co můžete a raději jej nepoužívejte vůbec. Ostatně, ani to není nutné. Pomocí ostatních rozhodovacích příkazů se tomu lze vždy vyhnout. A teď k příkazu.

Za příkazem goto je název návěští, na které má program skočit. Návěští se v programu určuje jednoduše tak, že se zapíše jeho jméno a za ním dvojtečka. Můžete skákat pouze na návěstí v rámci jedné funkce. A jak by řekl Spok, z logiky věci vyplývá, že nemohou být dvě návěstí stejného jména.

  1. /*------------------------------------------------*/
  2. /* c12/goto.c                                     */
  3.  
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8.     char znak;
  9.  
  10.     printf("Opravdu chcete smazat vsechny data na disku? [a/n/k]> ");
  11.     znak = (char) getchar();
  12.  
  13.     if ((znak == 'a') || (znak == 'A'))
  14.         goto ano;
  15.     if (znak == 'n')
  16.         goto ne;
  17.     if (znak == 'N')
  18.         goto ne2;
  19.     if (znak == 'k')
  20.         goto konec2;
  21.  
  22.     printf("Mel si zmacknout \'a\',\'n\' nebo \'k\' a ne \'%c\'\n", znak);
  23.   konec2:
  24.     return 0;
  25.  
  26.   ne2:
  27.     printf("Stejne smazu co muzu!\n");
  28.     goto konec;
  29.   ne:
  30.     printf("Nechcete? Smula!\n");
  31.  
  32.   ano:
  33.     printf("Data byla smazana !!!\n");
  34.   konec:
  35.     printf("Ne, nebojte, to byl jenom zertik\n");
  36.     return 0;
  37. }
  38.  
  39. /*------------------------------------------------*/

Cyklus for

Pomocí cyklů můžete vykonávat nějakou činnost opakovaně. Můžete cyklit buďto jeden příkaz, nebo nějaký blok příkazů. Pokud chcete cyklus z nějakého důvodu ukončit, můžete v bloku uvést příkaz break. Cyklus se tak ukončí a program pokračuje příkazy za tělem cyklu. Jinou možností je příkaz continue. Tento příkaz přeruší vykonávání dalších příkazů v těle cyklu a program skočí na začátek cyklu. Podívejte se na cyklus for.

for (inicializace; podmínka; výraz)
       telo cyklu

Cyklus for provádí příkazy v těle bloku tak dlouho, dokud platí podmínka. Tato podmínka může být jakýkoliv výraz vracející celočíselnou hodnotu. Může obsahovat konstanty, proměnné, relační operátory (==,>,|| atp.) a není povinná. V případě, že žádnou podmínku neuvedete, překladač za ní dosadí číslo jedna, což bude mít za následek věčné provádění cyklu. Cyklus ale pořád můžete ukončit pomocí break. Podmínka se vyhodnocuje před každým začátkem cyklu (i po příkazu continue). Pokud se hned na poprvé vyhodnotí jako FALSE, pak tělo cyklu neproběhne ani jednou.

Část inicializace také není povinná. Může v ní být libovolný výraz. Tato část proběhne jen jednou (před začátkem cyklu). Klidně byste inicializaci mohli přesunout před příkaz for, ale pokud se inicializace týká cyklu for, je čitelnější ji použít uvnitř for.

Poslední část v příkazu označená jako výraz se provádí po každém ukončení cyklu (i příkazem continue), ale už ne po skončení cyklu (třeba příkazem break). Taktéž není povinná.

Typické použití cyklu for:

  1. /*------------------------------------------------*/
  2. /* c12/for.c                                      */
  3. #include <stdio.h>
  4.  
  5. int main(void)
  6. {
  7.     int x;
  8.  
  9.     for (x = 1; x < 10; x++) {
  10.         if ((x % 2) != 0)
  11.             continue;
  12.         printf("Sude cislo: %i\n", x);
  13.     }
  14.  
  15.     printf("\nA nebo trochu jinak:\n\n");
  16.     x = 1;
  17.     for (;;) {
  18.         if (x >= 10)
  19.             break;
  20.         if ((x % 2) == 0)
  21.             printf("Sude cislo: %i\n", x);
  22.         x++;
  23.     }
  24.     return 0;
  25. }
  26.  
  27.  /*------------------------------------------------*/

Výstup z programu:

Sude cislo: 2
Sude cislo: 4
Sude cislo: 6
Sude cislo: 8

A nebo trochu jinak:

Sude cislo: 2
Sude cislo: 4
Sude cislo: 6
Sude cislo: 8

Cyklus for se také často využívá při práci s poli. Například inicializace pole samými nulami (všimněte si indexování pole od 0 do N-1):

#define N 100
int x, pole [N];

for (x = 0; x <= N - 1;x++)
      pole[x] = 0;

Cykly do a while

Cyklus do má tu vlastnost, že proběhne alespoň jednou. Je ukončen klíčovým slovem while, za kterým je podmínka v kulatých závorkách a platí o ní totéž, jako u cyklu for. Tato podmínka se vyhodnocuje až po průchodu cyklem.

V cyklu do můžete pro řízení chodu programu využít příkazů break i continue. Tělo cyklu může být jeden příkaz nebo blok (uzavřené příkazy ve složených závorkách).

do
      telo cyklu
while ( podminka );

Cyklus while je podobný, jen s tím rozdílem, že se podmínka vyhodnocuje před provedením cyklu.

while ( podminka )
      telo cyklu

Zda použijete cyklus for, do nebo while záleží většinou jen na vaší fantazii a preferenci. Máte 3 způsoby, jak udělat totéž.

  1. /*------------------------------------------------*/
  2. /* c12/while.c                                    */
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8.     int iterace = -1;
  9.     int navrat;
  10.     do {
  11.         printf("Zadejte pocet iteraci <O,10>: ");
  12.         navrat = scanf("%i", &iterace);
  13.         if (!navrat)
  14.             return 1;           /* uzivatel nezadal cislo; navrat == 0 */
  15.         if (navrat == EOF) {
  16.             printf("stdin je uzavren.\n");
  17.             return 1;
  18.         }
  19.     } while ((iterace < 0) || (iterace > 10));
  20.  
  21.     while (iterace) {
  22.         printf("%i ", iterace--);
  23.     }
  24.  
  25.     printf("\n");
  26.     return 0;
  27. }
  28.  
  29. /*------------------------------------------------*/

Možný výstup:

Zadejte pocet iteraci <0,10>: -5
Zadejte pocet iteraci <0,10>: 15
Zadejte pocet iteraci <0,10>: 5
5 4 3 2 1

Lomené závorky v cyklu while v příkladu jsou sice nadbytečné (protože cyklus obsahuje jen jeden příkaz), ale zlepšují čitelnost kódu a snižuje se riziko chyby při úpravě zdrojového kódu.
Častou chybou je totiž přidání příkazu za příkaz u cyklu (nebo i podmínky if) a zapomenutí uzavření těchto příkazů do bloku. Jako v následujícím fragmentu kódu, kde se vytiskne 10x písmeno „X“ a jen jedenkrát „Y“.

for (i = 0; i < 10; i++)
     printf("X");
     printf("Y");

Tak bacha na to, mí věrní c-čkaři!

Komentář Hlášení chyby
Created: 29.8.2003
Last updated: 19.6.2014
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..