Předchozí kapitola

Obsah

Konec

Následující kapitola

6. Řízení chodu programu

6.1. Výrazový příkaz
6.2.
Prázdný příkaz
6.3.
Bloky.
6.4.
Oblast platnosti identifikátoru
6.5.
Podmíněný příkaz if-else.
6.6.
Přepínač
6.7.
Cykly

Cyklus while
Cyklus for
Cyklus do

6.8. Příkaz skoku


V dosavadním výkladu jsme se dozvěděli, že se program skládá z funkce main() a z příkazů, které tato funkce obsahuje. Své intuitivní představy o programu rozšíříme nejen o případné další funkce, ale detailněji1 se podíváme i na obsah funkcí. Tělo funkce obsahuje řadu příkazů. Naším cílem je provádět právě ty příkazy, které odpovídají zvolenému záměru. Výběr z příkazů je určen stavem dosavadního běhu programu, vstupními údaji a řídícími strukturami, které jsme použili.

Program, provádějící příkazy v pevném a neměnném pořadí, které odpovídá jejich umístění ve zdrojovém textu a navíc bez možnosti jejich výběru, jistě není naším ideálem. A to bez ohledu na zvolený vyšší programovací jazyk. Cílem této kapitoly je proto seznámení s řídícími strukturami.

Před podrobným přístupem nejprve uveďme přehledně příkazy, které máme v C k dispozici:

6.1. Výrazový příkazzpet

Výraz známe z předchozího textu. Výrazem je nejen aritmetický výraz (například a + b, či 5 + 1.23 * a), prostý výskyt konstanty (literálu) či proměnné, ale i funkční volání a přiřazení. Jestliže výraz ukončíme symbolem ; (středník), získáme výrazový příkaz.

6.2. Prázdný příkazzpet

Prázdný příkaz je výrazový příkaz, v němž není výrazová část. Tato konstrukce není tak nesmyslná, jak se na první pohled může zdát. Dává nám v některých případech možnost umístit nadbytečný středník ; do zdrojového textu. Například i za vnořený blok. Protože se o prázdném příkazu můžeme těžko dozvědět něco dalšího, podívejme se raději na další příkazy jazyka C.

6.3. Bloky.zpet

Všude v C, kde se může vyskytovat příkaz, se může vyskytovat i složený příkaz. Složený příkaz je posloupností příkazů2. Konstrukce, která složený příkaz vymezuje, začíná levou a končí pravou složenou závorkou { }, a nazývá se blok. V bloku můžeme, kromě již zmíněné realizace složeného příkazu, provádět lokální deklarace a definice. Ty ovšem pouze na začátku bloku. Jejich platnost je omezena na blok a případné další vnořené bloky. Vnořený blok nemusí být ukončen středníkem. Představuje složený příkaz a jeho konec je jasně určen. Není na škodu si uvědomit, že tělo každé funkce je blokem. Proto jsme mohli v těle funkce main() definovat a používat lokální proměnné. Syntakticky můžeme blok popsat následovně:

{
 [declaration_1[; declaration_2 ... ];]
 [statement_1 [; statement_2 ... ] ]
}

Blok tedy může obsahovat žádnou, jednu či více deklarací. Pokud deklaraci obsahuje, musí být od další části bloku oddělena středníkem. Dále blok může obsahovat jednotlivé příkazy, rovněž oddělené středníkem. Povšimněme si, že poslední z příkazů nemusí být od uzavírací složené závorky bloku středníkem oddělen.

6.4. Oblast platnosti identifikátoruzpet

Identifikátor, který deklarujeme či definujeme, si ponechává svou platnost v programu, v němž je deklarován či definován. Jeho jméno je v tomto rozsahu viditelné, není-li maskováno:

Jméno makra je platné od jeho definice (direktivou define) až do místa, kdy je definice odstraněna (direktivou undef, pokud vůbec odstraněna je). Jméno makra nemůže být maskováno3.

6.5. Podmíněný příkaz if-else. zpet

Operátor podmíněného výrazu ? : používáme pro výběr části výrazu. Pro výběr z příkazů máme k dispozici podmíněný příkaz. Mohli bychom říci, že se jedná o příkazy dva. Jejich syntaktický zápis je následující

if ( <expression> ) <statement1>;

if ( <expression> ) <statement1>;
else <statement2>;

Význam příkazu if je následující. Po vyhodnocení výrazu expression (musí být v závorkách) se v případě jeho nenulové hodnoty provede příkaz statement1. Po jeho provedení pokračuje program za tímto příkazem. V případě nulového výsledku výrazu se řízení programu předá bezprostředně za podmíněný příkaz. Jinak řečeno se příkaz statement1 přeskočí.

Příkaz if můžeme použít i s variantou else. Sémantika takového příkazu if-else je v první části totožná se samotným if. Je-li výslednou hodnotou výrazu expression jedna (nenulová hodnota), provede se příkaz statement1. V opačném případě, kdy výsledkem je nula, se provede příkaz statement2. V obou případech se řízení programu po provedení prvního, respektive druhého, příkazu předá za celý podmíněný výraz.

if

if-else

Někdy se výklad sémantiky předkládá způsobem, kdy se nulové hodnotě říká nepravda a nenulové (jedničce) pravda. Jak ostatně známe z logických výrazů. Pak lze ve druhé variantě říci, že je-li podmínka splněna, vykoná se první příkaz statement1, jinak příkaz druhý statement2.

Zdůrazněme, že oba příkazy jsou ukončeny středníkem4.

Z předchozích odstavců víme, že místo příkazu může být umístěn blok. V takovém případě je jasně příkaz vymezen a středník za blokem před else nepíšeme!

Příklad nám ukáže použití podmíněného příkazu if-else. Máme vypočíst a zobrazit podíl dvou zadaných racionálních čísel. Protože je známo, že nulou dělit nelze, využijeme podmíněný příkaz k ošetření tohoto omezení.

/**********************/
/* soubor if-else1.c  */
/**********************/

#include <stdio.h>

int main(void)
{
 float a, b;
 puts("Zadej dve racionalni cisla:");
 scanf("%f %f", &a, &b);

 if (b == 0.0)
       puts("\b\nNulou delit nelze!\n");
 else
 {
       float podil;
       podil = a / b;
       printf("Jejich podil je: %10.2f\n", podil);
 }
 return 0;
}

Jako první příkaz po testu if je jednoduchý výstup řetězce puts(). Je ukončen středníkem a následuje klíčové slovo else. Příkaz v této části je tvořen blokem. Za blokem středník není. Pokud by tam byl, byl by chápán jako prázdný příkaz. Tak je interpretován i středník za posledním příkazem v bloku.

V úvodu této kapitoly jsme si shrnuli příkazy jazyka C. Nepřekvapí nás tedy umístění podmíněného příkazu if jako jednoho z příkazů if-else. Zpravidla se umisťuje na místo druhého příkazu. Výsledná konstrukce bývá často nazývána if-else-if5:

if ( <expression1> )  <statement1>;
else if ( <expression2> )  <statement2>;
else if ( <expression3> )  <statement3>;
...
else if ( <expressionN> )  <statementN>;
else <statementN+1>;

Její často využívanou vlastností je skutečnost, že se provede právě jeden z příkazů. Pokud není poslední příkaz bez podmínky statementN+1 uveden, nemusí se provést žádný. Jinak řečeno, provede se nejvýše jeden.

Zapamatujme si dobře tuto konstrukci. Nejen pro její užitečnost. Po příkladu ji budeme moci porovnat s konstrukcí zvanou přepínač.

Naším úkolem je programově ošetřit, jaký alfanumerický znak byl zadán. Případně vydat zprávu o zadání znaku jiného. Protože je jisté, že zadaný znak patří právě do jedné z tříd malá písmena, velká písmena, číslice a jiné, použijeme konstrukci podmíněného příkazu. Při použití if-else by pro každou třídu byla znovu testována příslušnost načteného znaku. Bez ohledu, zdali znak již některou z předchozích podmínek splnil. Použitá konstrukce if-else-if případné nadbytečné testy odstraní. Vždy bude vybrán právě jednen z příkazů. Připomeňme si rovněž logický výraz, který tvoří podmínku

if ((znak >= 'a') && (znak <= 'z'))

Vnější závorky jsou nezbytné, neboť ohraničují podmínku příkazu if. Vnitřní závorky naopak uvést nemusíme. Priorita operátoru && je nižší než relačních operátorů <= a >=. Závorky jsme přesto uvedli, neboť zvyšují čitelnost. Celý výpis zdrojového textu následuje.

/**********************/
/* soubor if-else2.c  */
/**********************/

#include <stdio.h>

int main(void)
{
 int znak;
 printf("Zadej alfanumericky znak:");
 scanf("%c", &znak);

 printf("\nZadal jsi ");
 if ((znak >= 'a') && (znak <= 'z'))
       printf("male pismeno\n");
 else if ((znak >= 'A') && (znak <= 'Z'))
       printf("velke pismeno\n");
 else if ((znak >= '0') && (znak <= '9'))
       printf("cislici\n");
 else
       printf("\nNezadal jsi alfanumericky znak!\n");

 return 0;
}

6.6. Přepínačzpet

Přepínač slouží k rozdělení posloupnosti příkazů na části, následné vybrání a provedení některé, či některých z nich. Pokud nám tato formulace přepínač příliš nepřiblížila, pokusme se jinak.

Přepínač slouží k větvení výpočtu podle hodnoty celočíselného výrazu.

Syntakticky zapsaný příkaz přepínač má tento tvar:

switch ( <expression> ) <statement>
case <constant expression> :
default :

Syntaktickou definici přepínače ovšem musíme upřesnit. Po klíčovém slově switch následuje celočíselný výraz expression uzavřený v závorkách. Za ním je příkaz statement, zpravidla tvořený blokem. Blok představuje posloupnost příkazů, v níž mohou být umístěna návěští, jejichž syntaxi vidíme na druhé řádce definice.

Řídící příkaz tvoří celočíselný konstantní výraz6 constant expression uvozený klíčovým slovem case a ukončený dvoutečkou :. Jedno z návěští může být klíčovým slovem default, přirozeně ukončeným dvoutečkou :.

Sémantika přepínače je poměrně složitá. Popíšeme si jednotlivá pravidla, jimiž se řídí:

Program vyhodnotí konstantní výraz a jeho hodnotu porovnává s každým z case návěští přepínače. Návěští case může být obsaženo uvnitř jiných příkazů (v rámci přepínače), kromě případného vnořeného přepínače.

V jednom přepínači se nesmí používat dvě návěští se stejnou hodnotou.

Nastane-li shoda hodnoty case návěští s hodnotou switch výrazu expression, je přeneseno řízení programu na toto návěští. Jinak je řízení přeneseno na návěští default v rámci příslušného přepínače. Pro návěští default platí stejné zásady jako pro jiná návěští.

Pokud nenapíšeme default návěští a hodnota výrazu switch se nebude shodovat s žádným z návěští, bude řízení přeneseno na příkaz následující za přepínačem.

Pokud program při provádění prěpínače vykoná příkaz break, bude řízení přeneseno na příkaz následující za přepínačem. Příkaz break může být v rámci přepínače umístěn v libovolných příkazech, kromě případných vnořených do, for, switch nebo while příkazech7.

Přepínač switch může mít mnoho forem. Podívejme se na příklad, v němž použijeme jednu z nich. Naším úkolem je podat informaci o tom, jaká hodnota padla při simulovaném hodu kostky. Místo kostky použijeme generátor pseudonáhodných čísel (dále jim budeme říkat jen náhodná). Protože takovou elektronickou kostku budeme používat i v příkladu v následujícím odstavci, věnovaném cyklům, probereme si funkce s ní spojené podrobněji.

Pomocí funkce srand()8nastavíme posloupnost náhodných čísel. Jako počáteční hodnotu nemůžeme zvolit konstantu, neboť pak by byla generovaná posloupnost náhodných veličin vždy stejná. Proto potřebujeme vhodnou hodnotu, která bude při každém spuštění programu jiná. Takto vhodně se mění například čas. Proto použijeme jako argument návratovou hodnotu funkce time()9. Jiné záměry s funkcí time() nemáme, proto použijeme jako její argument NULL. Nesmíme jen zapomenout čas správně přetypovat. A generátor máme připraven:

srand((unsigned) time(NULL) );

Teď musíme umět kostkou házet. Při volání funkce rand() dostáváme náhodné celé číslo v rozsahu 0 až RAND_MAX. 16-ti bitové BC31 definuje tuto hodnotu jako MAXINT, konkrétně 32767. Pro naši potřebu stačí číslo v rozsahu 0 až 5, k němuž přičteme jedničku. Tuto práci odvede operace modulo šesti.

switch (rand() % 6 + 1)

Získaná hodnota 1 až 6 je výrazem přepínače switch. V závislosti na této hodnotě se řízení programu přenese na konstantní návěští. Problém je v tom, že pokud bychom takto označenou posloupnost příkazů neukončili příkazem break, pokračoval by od návěští program dále, až po konec přepínače. I této vlastnosti se dá využít. Ukázku vidíme ve spojeném hlášení pro hodnoty 2 a 3. Umístění návěští default v přepínači je libovolné. Zpravidla se píše na konec, což je poměrně zakořeněný zvyk. Budeme jej rovněž dodržovat.

/**********************/
/* soubor switch-1.c  */
/**********************/

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main(void)
{
 char *s;

 printf("\nHazim kostkou...\n");
 srand((unsigned) time(NULL) );

 switch (rand() % 6 + 1)
       {
        case 1: s = "jednicka";
               break;
        case 2:
        case 3: s = "dvojka nebo trojka";
               break;
        case 4: s = "ctyrka";
               break;
        case 5: s = "petka";
               break;
        default: s = "sestka";
               break;
        }
 printf("\nPadla %s.\n\n", s);

 return 0;
}

Srovnáme-li přepínač s konstrukcí if-else-if, vidíme zásadní rozdíly:

  1. Rozhodovací výraz if může testovat hodnoty jakéhokoliv typu10, zatímco rozhodovací výraz příkazu switch musí být výhradně celočíselným výrazem.
  2. V konstrukci if-else-if se provede nejvýše (či podle použití právě) jeden z příkazů. I u přepínače se nemusí provést žádný z příkazů. Může jich být ovšem provedeno více. Konstantní návěští určuje pouze první z nich. Pokud chceme, oddělíme následující příkazy příkazem break.
  3. V přepínači se návěští default může vyskytovat kdekoliv. Odpovídající varianta v konstrukci if­else­if může být umístěna pouze na konci11.

6.7. Cyklyzpet

Cyklus je část programu, která je v závislosti na podmínce prováděna opakovaně. U cyklu obvykle rozlišujeme řídící podmínku cyklu a tělo cyklu. Řídící podmínka cyklu určuje, bude-li provedeno tělo cyklu, či bude-li řízení předáno za příkaz cyklu. Tělo cyklu je příkaz, zpravidla v podobě bloku.

Cykly můžeme rozdělit podle toho, provede-li se tělo alespoň jedenkrát, a cykly, kde tělo nemusí být provedeno vůbec. Rozhodně můžeme říci, že i když v C existují různé typy cyklů, je možné vystačit s jedním z nich. Přesto si v této podkapitole povíme o všech. Jejich výběr ponecháme na vhodnosti použití v dané situaci i na jejich individuální oblibě. V části věnované příkazu while si popíšeme některé vlastnosti společné všem cyklům. Postupně tedy while, for a do.

Cyklus whilezpet

Příkaz while, vykonává příkaz statement (tělo cyklu) vícekrát, nebo vůbec, dokud má v daném kontextu testovací výraz expression nenulovou hodnotu. Syntaxe příkazu while je následující:

while ( <expression> ) <statement>

Příkaz je velmi často blokem. Zde se zejména začátečníci mylně domnívají, že pokud se kontext kdykoliv během provádění příkazů bloku těla cyklu změní, a podmínka tak přestane být splněna, je cyklus opuštěn. To ovšem není pravda. Pravidlo říká, že se příkaz statement provede, je-li podmínka expression splněna. Je-li příkazem blok, provede se tedy blok celý. Teprve poté je znovu proveden test. Názornější bude, podívat se na vývojový diagram.

cyklus while

Ve vývojovém diagramu se vyskytují nepovinné příkazy break a continue. S prvním z nich jsme se již setkali u přepínače. Druhý zatím neznáme. Jejich sémantiku, zakreslenou ve vývojovém diagramu, si nyní popíšeme. Předem poznamenejme, že oba jsou pokládány za prostředky strukturovaného programování.

Příkaz break má ve všech cyklech stejný význam. Ukončí provádění příkazů těla cyklu a předá řízení prvnímu příkazu za příkazem while. Tímto způsobem můžeme bezprostředně ukončit průběh cyklu bez ohledu na hodnotu podmínky.

Příkaz continue rovněž ukončí provádění příkazů těla cyklu. Řízení ovšem předá těsně před příkaz cyklu. Proběhne tedy vyhodnocení testovacího výrazu a podle jeho hodnoty pokračuje program stejně, jako by bylo tělo cyklu vykonáno do konce (tedy bez předčasného ukončení vykonáním continue). Podobně jako break, má příkaz continue ve všech cyklech stejný význam.

Použití příkazu while ukazuje příklad. Opět v něm používáme kostku. S ní související funkce jsme popsali v předchozím příkladu. Tentokrát je naším cílem sečíst počet pokusů, potřebných k tomu, abychom hodili šestku POCET-krát. Současně s novou látkou si připomeneme některé poznatky z předchozích kapitol.

Konstanta POCET12, která nemá uveden typ má implicitně typ int. Klíčovým slovem static použitým při deklaraci proměnných celkem a pocet (je to jiný identifikátor než POCET), nejen umisťujeme tyto proměnné do datového segmentu programu, ale zároveň je jim při spuštění programu definována počáteční hodnota nula. Díky tomu je dále nemusíme inicializovat.

Cyklus while samotný probíhá tak dlouho, dokud platí podmínka (pocet < POCET). Za blokem, tvořícím příkaz cyklu, nemusíme psát ukončovací středník. Následuje formátovaný standardní výstup výsledku a závěr funkce main().

/**********************/
/* soubor while-1.c   */
/**********************/

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

const POCET = 10;

int main(void)
{
 static int celkem, pocet;

 printf("\nHazim kostkou dokud mi nepadne %d-krat sestka. ...\n", POCET);
 srand((unsigned) time(NULL) );

 while (pocet < POCET)
  {
   celkem++;
   if ((rand() % 6 + 1) == 6)
        pocet++;
  }

 printf("\nA je to! Hodu bylo celkem %d.\n\n", celkem);

 return 0;
}

Následující příklad ukazuje použití příkazu while spolu s if-else-if a příkazy break a continue. Naším úkolem je porovnání dvou načtených řetězců a zobrazení menšího z nich. Tento příklad používá aritmetiku ukazatelů, probíranou v rámci jedné z dalších kapitol. V tomto okamžiku přijměme skutečnost, že prostřednictvím ukazatelů porovnáváme ASCII hodnoty jednotlivých znaků odpovídajících řetězců. Každý řetězec je ukončen znakem s kódem nula.

Zvolená mnemonika pojmenovává dva řetězce a a b, ukazatele na, respektive do, těchto řetězců pa a pb. Identifikátor pk představuje ukazatel na kratší z porovnávaných řetězců.

Algoritmus úlohy srovnává postupně znaky obou řetězců, dokud jsou si rovny:

else if (*pa == *pb)
      {
       pa++; pb++;
       continue;
      }

Použití continue není v našem případě nezbytné. Díky if-else-if nemůže v této větvi těla cyklu následovat jiný příkaz. Naopak break v obou zbývajících větvích opouští cyklus. Ukončuje tím srovnávání řetězců a určuje kratší z nich. V prvním ze zmíněných případů je

if (*pa < *pb)
      {
       pk = a;
       break;
      }

hodnota znaku v řetězci a menší, než v řetězci b. a je tudíž kratší. Cyklus je ukončen. Dále bychom totiž opustili definovaný rozsah řetězce a. A protože je podmínka cyklu napsána tak, že zajišťuje jeho opakování, dokud nenarazíme na konec alespoň jednoho z řetězců:

while ((*pa != 0x0) && (*pb != 0x0)) /* '\0' 0 */

Máme jistotu, že pokud nenastaly předchozí možnosti, musí platit:

else
{
pk = b;
break;
}

Tedy, že kratší je druhý řetězec. Těsně za tělem cyklu máme ošetřenu variantu, kdy skončí druhý z řetězců dříve, než se jejich obsahy lišily. Pak je kratším z řetězců. Nám nezbývá, než jej zobrazit a ukončit program. Následuje nepřerušený výpis zdrojového textu řešení.

/************************************************/
/* soubor cmp_str.c - compare two strings       */
/* porovna dva nactene retezce a zobrazi mensi  */
/************************************************/

#include <stdio.h>
#define DELKA 50

int main(void)
{
 char a[DELKA], b[DELKA], *pa, *pb, *pk; /* pa[], pb[], pk[] */

 pk = pa = gets(a); pb = gets(b);

 while ((*pa != 0x0) && (*pb != 0x0)) /* '\0' 0 0x0 - totez*/
   {
    if (*pa < *pb)
      {
       pk = a;
       break;
      }
    else if (*pa == *pb)
      {
       pa++; pb++;
       continue;
      }
    else
      {
       pk = b;
       break;
      }
   }


 if ((*pa != 0x0) && (*pb == 0x0))
	 pk = b;
 puts("\nkratsi je:");
 puts(pk);
 return 0;
}

Cyklus forzpet

Příkaz for provádí příkaz statement vícekrát, nebo vůbec, dokud je hodnota nepovinného testovacího výrazu expr2 nenulová. V příkazu for můžeme ještě napsat dva další výrazy s vedlejším efektem, expr1 a expr3. Syntaxe for:

for ( [<expr1>] ; [<expr2>] ; [<expr3>] ) <statement>

Nepovinný výraz expr1 je proveden před prvním vyhodnocením testu. Typicky se používá pro inicializaci proměnných před cyklem. Po každém provedení těla cyklu statement, provede program nepovinný výraz expr3. Typicky se jedná o přípravu další iterace cyklu. Pokud neuvedeme testovací výraz expr2, použije překladač hodnotu 1, a tedy bude provádět nekonečný cyklus. Naštěstí můžeme použít příkazy break a continue se stejným významem, jaký jsme popsali u while.

Vývojový diagram úplné varianty příkazu for je na obrázku, v němž jsme ponechali značení odpovídající výše uvedené syntaxi:

cyklus for

Pro tisk ASCII tabulky znaků s kódy 32 až 127 použijeme příkaz for v úplné variantě:

for (znak = ' '; znak < 128; znak++)

V tomto případě můžeme nazývat proměnnou znak řídící proměnnou cyklu. Před testem ji inicializujeme mezerou13, tedy prvním zobrazitelným ASCII znakem. Následuje test na hodnotu 128. Provádí se před každým průchodem tělem cyklu. Není-li podmínka splněna, pokračuje se za cyklem.

Po každém průchodu tělem cyklu je řídící proměnná cyklu znak zvýšena o jedničku. Tak je připraven další průchod cyklem s hodnotou následného ASCII znaku. Samotné tělo cyklu vlastně jen zobrazuje jednotlivý znak. Je-li jeho ASCII hodnota dělitelná 16 beze zbytku, zajistí vysláním konce řádku tvar tabulky.

/**********************/
/* soubor for-1.c     */
/**********************/

#include <stdio.h>

int main(void)
{
 int znak;

 putchar('\n');
 for (znak = ' '; znak < 128; znak++)
  {
   if (znak % 16 == 0)
	putchar('\n');
   putchar(znak);
  }
 putchar('\n');
 return 0;
}

/*

 !"#$%&'()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~&127

*/

Cyklus dozpet

Příkaz do je jediným z cyklů, který zajišťuje alespoň jedno provedení těla cyklu. Jinak řečeno, jeho testovací příkaz statement je testován až po průchodu tělem cyklu. Pokud je test splněn, provádí se tělo cyklu. Po syntaktické stránce tvoří tělo cyklu opět výraz expression:

do <statement> while ( <expression> );

Vývojový diagram příkazu do tuto vlastnost ukazuje ještě názorněji:

cyklus do

Příkazy break a continue v těle cyklu se chovají stejně, jaako v cyklech ostatních. Po break je cyklus opuštěn, zatímco po continue je proveden výraz expression a podle jeho výsledku se pokračuje dále.

Časté použití příkazu do nalezneme například v těch numerických výpočtech, kdy je alespoň jedna iterace nezbytná. V našem případě se jedná o výpočet přibližné hodnoty Eulerova čísla. V prvním přiblížení položíme hodnotu e rovnu jedné stejně, jako hodnotu přírůstku epsilon. Za upozornění stojí datový typ long double, a tedy i odpovídající vhodné určení typů číselných konstant. I ve formátovaném výstupu jsme museli specifikovat příslušný datový typ. Operátory přiřazení spojené s operací nad svou levou stranou jsou použity spíše pro připomenutí jejich existence.

/**********************/
/* soubor dowhile2.c  */
/**********************/

#include <stdio.h>
#include <math.h>

#define chyba 1.0e-15L

int main(void)
{
 long double e = 1.0L, epsilon = 1.0L;
 unsigned long n = 0L;


 printf("\n");
 do
  {
   epsilon /= ++n;
   e += epsilon;
  } while (epsilon > chyba);
 printf("e=%21.18Lf\tpocet diteraci:%ld", e, n);

 return 0;
}

6.8. Příkaz skokuzpet

Příkaz skoku patří k zatracovaným příkazům strukturovaného programování. Skutečně s jeho pomocí dokáže méně zkušený programátor vytvořit zdrojový text, pro nějž se vžilo označení špagetový. Skutečnost, že jsme si příkaz skoku nezatajili má tři příčiny:

  1. Jazyk C prostě příkaz skoku obsahuje. Jeho neuvedení by nebylo fér.
  2. Jazyk C umožňuje jako systémový prostředek psát programy na velmi nízké úrovni. Často velmi blízko strojovému kódu. Použití skoku v takovém případě může být velmi žádoucí14.
  3. Skokové příkazy break, continue a return15 jsou označovány jako strukturované skoky. Jejich použití teorie (ani praxe) programování neodmítá.

Zůstává nám tedy jen příkaz skoku goto. Jeho syntaxe je jednoduchá:

identifier: <statement> ;
goto <identifier> ;

Uvedli jsme si nejen syntaxi goto samotného, ale i souvisejícího návěští identifier. Návěští neprovádí žádnou akci a nemá jiný vliv na řízení běhu. Jeho identifikátor jen označuje nějaké místo ve zdrojovém textu. Příkaz goto přenáší řízení na příkaz, označený návěštím. V rámci jedné funkce nesmí být dvě stejná návěští.


Výsvětlivky:

1 V tomto skriptu věnujeme funkcím celou kapitolu.
2 Nezapomeňme, jednotlivé příkazy jsou ukončeny středníky.
3 To je jasné, uvědomíme-li si, že makra zpracovává preprocesor. Teprve takto zpracovaný text s expandovanými makry je vstupem pro samotný překladač.
4 Jinak by se o příkazy nejednalo.
5 Některé programovací jazyky kvůli ní dokonce zavádí klíčové slovo elif.
6 Tedy výraz vyčíslitelný během překladu. K celočíselným výrazům patří i znakové výrazy.
7 Tyto příkazy mohou break obsahovat. Jeho význam pak ovšem bude spojen s nimi, nikoliv s vnějším příkazem case.
8 Její prototyp je umístěn v hlavičkovém souboru stdlib.h.
9 Její prototyp je umístěn v hlavičkovém souboru time.h.
10 Je vhodné, aby výsledkem testu byla celočíselná hodnota, nejlépe 0 a 1. Jinak automaticky proběhne konverze.
11 Případně jen na začátku. To bychom museli neobvykle negovat podmínky.
12 Díky tomu, že je definována mimo tělo funkce je rozsahem její platnosti celý soubor (počínaje místem definice).
13 ASCII kód mezery je 32.
14 Přeložený tvar našich "pěkných strukturovaných programů" skoky ostatně stejně obsahuje.
15 První dva jsme již popsali dříve v této kapitole. Příkazu return se věnujeme v souvislosti s funkcemi.


Předchozí kapitola

Obsah

Začátek

Následující kapitola


Název: Programování v jazyce C
Autor: Petr Šaloun
Do HTML převedl: Kristian Wiglasz
Poslední úprava: 19.11.1996