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í:
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;
}
/*------------------------------------------------*/
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í).
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.
- /*------------------------------------------------*/
- /* c12/switch.c */
- #include <stdio.h>
- {
- char znak;
- #ifdef __unix__
- #endif
- }
- }
- /*------------------------------------------------*/
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.
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
.
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:
- /*------------------------------------------------*/
- /* c12/for.c */
- #include <stdio.h>
- {
- int x;
- }
- x = 1;
- x++;
- }
- }
- /*------------------------------------------------*/
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):
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).
Cyklus while
je podobný,
jen s tím rozdílem, že se podmínka vyhodnocuje před provedením 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éž.
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“.
Tak bacha na to, mí věrní c-čkaři!