Ukazatele na funkce

V této kapitole zakončím výklad syntaxe jazyka C (no fakt, už je koneec! :D). Vysvětlím vám ukazatele na funkce a pak si zopakujete základní deklarace proměnných a funkcí. V jazyce C existují ještě další konstrukce, které jsem nevysvětloval (například jak použít assembler), ale troufám si tvrdit, že s většinou potřebných konstrukcí jsem vás již seznámil. Příklad na konci bude shrnovat většinu probrané látky. Ale ještě neodcházejte. Výklad jazyka C tím zdaleka nekončí. Jazyk C obsahuje řadu standardních knihoven, jejichž znalost je pro programování v C zásadní. V dalším výkladu se s nimi budete seznamovat.

Ukazatele na funkce

Ukazatele na funkce jsem si nechal schválně až na konec, protože snažit se pochopit jejich zápis je opravdu sado maso. Doufám, že už dobře zvládáte samotné ukazatele a také ukazatele na ukazatele.

Protože i funkce programu leží kdesi v paměti, existuje adresa, která ukazuje na začátek této funkce. Nyní se konečně dozvíte, proč musí být za jménem funkce ve zdrojovém kódu kulaté závorky, i když nečeká funkce žádné argumenty. Je to proto, že samotné jméno funkce (bez kulatých závorek) je překladačem chápáno jako adresa funkce, tedy nějaké číslo! Takové číslo lze i vytisknout, ale to by asi k ničemu nebylo. Pomocí ukazatele na funkci lze funkci zavolat (a to i s příslušnými argumenty). Můžete tak kupříkladu napsat funkci, která bude mít jako argument ukazatel na jinou funkci, kterou pak může spustit. Podívejte se, jak vypadá deklarace ukazatele na funkci. Jde o deklaraci ukazatele (jmeno) na funkci, která má návratovou hodnotu typu typ. V závorce mohou být uvedené typy očekávaných argumentů funkce.

typ (*jmeno)([typ_arg,..]);

Kulaté závorky kolem jména funkce a hvězdičky jsou nutné, jinak by to vypadalo jen jako deklarace funkce, která má jako návratový typ typ *.

V příkladu níže deklaruji ukazatel uknf, který ukazuje na funkci bez argumentů s návratovou hodnotou „ukazatel na typ char“. Pro srovnání máte hned za touto deklarací deklaraci oné funkce. Všimněte si, že závorky při deklaraci ukazatele na funkci jsou vždy nevyhnutelné. Kdybych je v tomto příkladu nepoužil, vytvořil bych deklaraci funkce s návratovým typem „ukazatel na ukazatel na typ char“.

/* deklarace ukazatele na funkci, která vrací ukazatel */
char *(*uknf) (void);
/* deklarace funkce, která vracejí ukazatel */
char *uknf (void);

V příkladu použiji novou funkci exit(), která ukončí program v jakémkoliv místě, s návratovou hodnotou, která je argumentem funkce exit().

Tato funkce je definována v souboru <stdlib.h>. Tam se o ní dočtete více.

/*------------------------------------------------*/
/* c17/ukafce.c                                   */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

#ifdef _MSC_VER
       #define SZU "lu"
#else
       #define SZU "zu"
#endif

/* nejdrive vytvorim strukturu, ktera bude obsahovat pojmenovani
 * funkce pro uzivatele programu, cislo funkce, bude zaznamenavat
 * pocet spusteni funkce a bude obsahovat ukazatel na funkci */

typedef struct {
    unsigned int cislo;
    char nazev[50];
    unsigned int spusteno;
    void (*ukfce) (unsigned int *, char *);
} robot;

/* nyni definuji funkce programu, ktere budou uzivateli nabizeny
 * za pomoci predchozi struktury */

/**
 * zobrazi pouze velka pismena ASCII tabulky
 */

void velka_pismena(unsigned int *spusteno, char *v)
{
    size_t i = 0;
    (*spusteno)++;
    do {
        if ((v[i] >= 'A') && (v[i] <= 'Z')) {
            printf("%c", v[i]);
        }
    } while (v[i++]);
    printf("\n");
}

/**
* zobrazi pouze mala pismena ASCII tabulky
*/

void mala_pismena(unsigned int *spusteno, char *v)
{
    size_t i = 0;
    (*spusteno)++;
    do {
        if ((v[i] >= 'a') && (v[i] <= 'z')) {
            printf("%c", v[i]);
        }
    } while (v[i++]);
    printf("\n");
}

/**
* zobrazi vse, co neni pismeno
*/

void nepismena(unsigned int *spusteno, char *v)
{
    size_t i = 0;
    (*spusteno)++;
    do {
        if (!(((v[i] >= 'a') && (v[i] <= 'z')) ||
              ((v[i] >= 'A') && (v[i] <= 'Z')))) {
            printf("%c", v[i]);
        }
    } while (v[i++]);
    printf("\n");
}

/**
* zobrazi text pozpatku
*/

void obrat_text(unsigned int *spusteno, char *v)
{
    size_t i = 0;
    if (*spusteno >= 2) {
        printf("Pro dalsi pouziti teto funkce se musite registrovat!\n");
        return;
    }
    (*spusteno)++;

    while (v[++i]);             /* hledam konec retezce */

    for (; i > 0; i--)
        printf("%c", v[i-1]);

    printf("\n");
}

/**
* vlozi do textu mezery
*/

void roztahni(unsigned int *spusteno, char *v)
{
    size_t i = 0;
    (*spusteno)++;
    while (v[i]) {
        printf("%c ", v[i++]);
    };
    printf("\n");
}
/**
* Funkce exit() ma jine parametry, nez ktere ocekava nas ukazatel,
* proto jsem vytvoril funkci konec, ktera simuluje potrebu
* deklarovanych argumentu
*/

void konec(unsigned int *spusteno, char *v)
{
    /* ukonci program s navratovou hodnotou 0, ktera znaci uspech */
    exit(0);
}


/**
* funkce vlozim do pole struktury robot
* spolu s nazvem pro uzivatele atd.
*/

robot *inicializuj(void)
{
    /* pokud by promenna "r" nebyla staticka, pak by po skonceni
       volani funkce prestala existovat! */

    static robot r[] = {
            {1, "Velke pismena",   0, velka_pismena},
            {1, "Mala pismena",    0, mala_pismena},
            {2, "Co neni pismeno", 0, nepismena},
            {3, "Obraceni textu",  0, obrat_text},
            {4, "Roztazeni textu", 0, roztahni},
            {5, "Ukonceni",        0, konec},
            {0, "", 0, NULL} /* "zarazka", podle ktere poznam,
                                ze jsem na konci pole */

    };
    return r;
}

int main(void)
{
    size_t i; int navrat;
    /** ukazatel na pole struktury robot */
    robot *rb;
    /**
     * funkce jsou ve strukture ocislovany,
     *  dle tohoto cisla budou vybirany */

    size_t cislo;
    /* maximalne 50 znaku + '\0' */
    char retezec[51];

   /* funkce inicializuj() vraci ukazatel na svou
      statickou promennou. At ji zavolate kolikrat
      chcete, bude ukazovat stale na to same pole.
      O tom, jak vytvaret nove promenne za behu programu
      bude rec v dalsi kaptile pozdeji. */

    rb = inicializuj();
    do {
        i = 0;
        do {
            /* zobrazeni menu - snadne a prehledne */
            printf("%2i)\t%s\t(%2i)\n", rb[i].cislo, rb[i].nazev, rb[i].spusteno);
        } while (rb[++i].ukfce != NULL);

        printf("Zadejte cislo z menu a retezec: ");

        /* vysledek prirazeni muzeme pouzit jako hodnotu
           (tak ho rovnou porovname s EOF) */

        if ((navrat = scanf("%" SZU " %50s", &cislo, retezec)) == EOF)
            break;
        else if (navrat != 2) { /* chybne nacteni polozek */
            printf("\n Chyba vstupu (%i)!\n", navrat);
            /* Zrejme nebylo zadano jako prvni cislo, ale nejaky retezec.
             * Ten se musi nyni nacist, jinak by se jej predchozi funkce
             * scanf pokousela v cyklu neustale nacitat jako cislo a tim
             * by cyklus nikdy neskoncil.
             * Promenna retezec muze obsahnout maximalne 50 znaku, proto
             * funkci scanf v prvnim argumentu urcime max. delku
             * nacitaneho retezce. */

            scanf("%50s", retezec); // nacteme "smeti"
            continue;
        }

        i = 0;
        /* hleda se sruktura uzivatelem zadaneho cisla */
        while (rb[i].ukfce != NULL) {
            if (rb[i].cislo == cislo) {

                /* VOLANI FUNKCE PRES UKAZATEL */
                rb[i].ukfce(&rb[i].spusteno, retezec);

                /* prochazi se cele pole, takze se spusti vsechny polozky v
                 * poli, ktere maji rb[].cislo == cislo */

                /* kdyby zde byl prikaz break, provedla by se prvni nalezena
                 * polozka a dale by se jiz nic neprohledavalo */

            }
            i++;
        }
    } while (1); /* nekonecny cyklus, ukonci se jen pomoci return nebo
                    break (nebo exit ukonci program) */


    printf("\nAstalavista baby!\n");
    return 0;
}

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

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

Důvod definování SZU viz datový typ pro ukazatel.

Možný výstup z programu:

 1)     Velke pismena   ( 0)
 1)     Mala pismena    ( 0)
 2)     Co neni pismeno ( 0)
 3)     Obraceni textu  ( 0)
 4)     Roztazeni textu ( 0)
 5)     Ukonceni        ( 0)
Vyber polozku dle cisla a zadejte retezec: 1 VelkaAMalaPismena
VAMP
elkaalaismena
 1)     Velke pismena   ( 1)
 1)     Mala pismena    ( 1)
 2)     Co neni pismeno ( 0)
 3)     Obraceni textu  ( 0)
 4)     Roztazeni textu ( 0)
 5)     Ukonceni        ( 0)
Vyber polozku dle cisla a zadejte retezec: 5
 konec

Program má jeden drobný nedostatek, a to, že při volání funkce k ukončení programu (je pod číslem 5) musíte zadat za číslo 5 ještě nějaký zbytečný řetězec. Určitě vás napadne spousta možností, jak se tohoto nedostatku zbavit.

Důležitější je, jak snadno se do takového programu přidá další funkce. Stačí jí jen napsat a ve funkci inicializuj() ji přidat do pole struktury robot. Nic víc se na programu měnit nemusí.

Ukazatel na funkci se může s výhodou použít v programech, které načítají funkce z knihoven. Při vytvoření funkce stačí jen změnit nebo vytvořit novou knihovnu pro program (a ne hned překládat celý program). Program si jí načte do paměti a pak s ní může snadno pracovat pomocí ukazatele, jako by ji měl odedávna. (Vytváření knihoven se budu věnovat až v části o programování v Linuxu.)

Přehled známých deklarací

Zde se můžete v přehledu podívat na deklarace, kterým byste měli rozumět. (Zopakujte si alespoň Ukazatele a pole.)

Deklarace základních typů, ukazatelů a funkcí
Deklarace Význam
typ jmeno; Proměnná určeného typu
typ *jmeno; Ukazatel na určitý typ (respektive pole daného typu)
typ jmeno[]; Konstantní ukazatel na pole daného typu (neznámo jak dlouhé)
typ jmeno[10]; Konstantní ukazatel na pole daného typu o velikosti 10-ti proměnných daného typu.
typ **jmeno; Ukazatel na ukazatel na daný typ
typ *jmeno[]; Konstantní ukazatel na pole ukazatelů daného typu
typ jmeno[][]; Konstantní ukazatel na pole konstantních ukazatelů na pole daného typu.
typ jmeno(); Deklarace funkce vracející typ
typ *(jmeno()); Funkce vracející ukazatel na typ. Zvýrazněné závorky jsou nadbytečné.
typ (*jmeno)() Ukazatel na funkci bez parametrů vracející typ
typ *(*jmeno)() Ukazatel na funkci bez parametrů vracející ukazatel na typ

Lze vytvářet i daleko složitější deklarace. Například:

unsigned long *(*jmeno[5][4])(char *);

Toto je dvojrozměrné pole obsahující ukazatele na funkci, jejíž návratová hodnota je ukazatel na typ unsigned long a argumentem této funkce je ukazatel na typ char. Sranda, ne? :D

Rozluštit takovéto zápisy není zrovna legrace. Proto doporučuji využívat typedef pro zjednodušení takovýchto konstrukcí. Předchozí proměnnou jmeno lze definovat takto:

typedef unsigned long *(*ukazatel_na_fci1)(char *);
ukazatel_na_fci1 jmeno[5][4];

To už je určitě daleko čitelnější. A myslím, že vám to pomůže i lépe pochopit předchozí zápis.

Pokud si nebudete někdy nějakým zápisem jistí, můžete využít program cdecl:

$ cdecl
Type `help' or `?' for help

cdecl> explain unsigned long *(*jmeno[5][4])(char *);
declare jmeno as array 5 of array 4 of pointer to function (pointer to char) returning pointer to unsigned long

Opakování – příklad

Je na čase si zopakovat probranou látku. Program, který je zde popsán obsahuje většinu probrané látky. Měli byste být schopni nejenom takovýto zdrojový kód přečíst a pochopit, ale i sami napsat. Programování se naučíte nejlépe tím, že si budete vymýšlet vlastní příklady (stále těžší a těžší) a programovat je. Snad sem vás dokázal během výuky dostatečně inspirovat.

Naprogramujeme si chůzi opilce. Program si přečte z příkazové řádky pravděpodobnosti, s jakými půjde opilec vpřed a vzad. Pravděpodobnost, že zůstane stát si dopočte program sám (zbytek do 100%).

Jelikož na příkazové řádce jsou i čísla chápána jako text, použiji funkci atoi(), ze standardní knihovny, na převod řetězce na číslo typu int. Po spuštění programu bude mít navíc uživatel možnost vybrat si z několika zobrazení opilce. Jakým způsobem se bude opilec zobrazovat se pokusím naprogramovat tak, jako by šlo o modul programu. Proto funkce pro zobrazování opilce budou v jiném souboru, než hlavní program a budou se volat pomocí ukazatele na funkce.

Začnu třemi hlavičkovými soubory: "dos1.h", "windows1.h" a "unix1.h". Tyto soubory jsem již použil v kapitole o uživatelských knihovnách. Jsou v nich deklarovány funkce implementačně závislé.

/*------------------------------------------------*/
/* c17/dos1.h                                     */
#include <dos.h>

void cekej(unsigned int cas)
{
    delay(cas);
}

/*------------------------------------------------*/
/*------------------------------------------------*/
/* c17/windows1.h                                 */
#include <windows.h>

void cekej(unsigned int cas)
{
    Sleep(cas);
}

/*------------------------------------------------*/
/*------------------------------------------------*/
/* c17/unix1.h                                    */
#include <unistd.h>

void cekej(unsigned int cas)
{
    usleep((unsigned long) cas*1000);
}

/*------------------------------------------------*/

V souboru "define1.h" definuji potřebné datové typy a makra. Tyto datové typy a makra se budou používat i v dalších knihovnách, proto je třeba zajistit, aby se nenačítal obsah tohoto soboru vícekrát (pomocí makra _DEFINE1_H, viz zdrojový kód).
Výčtový typ smer bude sloužit k určování směru chůze opilce, struktura pohyb bude obsahovat pravděpodobnosti, s jakou půjde opilec vpřed, vzad nebo zůstane stát a také pozici, kde opilec právě stojí. Všimněte si, že položka stop nebude ani využita. Lze jí dopočítat do 100% pomocí položek vpred a vzad.
Je otázkou, zda je lepší vytvořit takovou položku, do které se hodnota jednou uloží a pak už se jen používá, nebo kdykoliv je v programu potřeba, tak se vypočte výrazem (100-vpred-vzad).
Vzhledem k tomu, že by se tento výraz musel v programu vícekrát počítat (což program zpomaluje) a i tento výraz zabírá v programu místo (instrukce pro výpočet), je asi výhodnější položku stop použít.
V tomto příkladě se však hodnota stop nepoužije vůbec, takže je opravdu zbytečná.

  1. /*------------------------------------------------*/
  2. /* c17/define1.h                                  */
  3.  
  4. #ifndef _DEFINE1_H
  5. #define _DEFINE1_H 1
  6.  
  7. #define DELKACHUZE 40
  8.  
  9.     dopredu, dozadu, stat
  10. } smer;
  11.  
  12.     int vpred, vzad, stop;
  13.     int pozice;
  14. } pohyb;
  15.  
  16. #endif
  17. /*------------------------------------------------*/

V souboru "zobraz1.h"1) jsou definovány všechny funkce, kterými se zobrazuje pohyb opilce. (Všimněte si, jak makra TISKNIOPILCE pokračují za zpětným lomítkem na druhé řádce.)
Ukazatele na tyto funkce jsem uložil do pole zobraz. To, která funkce se bude volat, nechám v programu na náhodě. Některé funkce jsem tam tak vložil záměrně vícekrát, aby byla větší pravděpodobnost, že budou vybrány. Naproti tomu jsem tam jednu funkci nevložil vůbec.

  1. /*------------------------------------------------*/
  2. /* c17/zobraz1.h                                  */
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include "define1.h"
  7.  
  8. #define TISKNIOPILCER(ZNK) printf("\r%3i%% (%*s%*c %3i%% (%3i)",p->vzad,\
  9.                   p->pozice, ZNK, DELKACHUZE - p->pozice,')',p->vpred,pocet);
  10. #define TISKNIOPILCEN(ZNK) printf("\n%3i%% (%*s%*c %3i%% (%3i)",p->vzad,\
  11.                   p->pozice, ZNK, DELKACHUZE - p->pozice,')',p->vpred,pocet);
  12.  
  13. void zobraz_opilce1(pohyb *, const smer, const int);
  14. void zobraz_opilce2(pohyb *, const smer, const int);
  15. void zobraz_opilce3(pohyb *, const smer, const int);
  16. void zobraz_opilce4(pohyb *, const smer, const int);
  17.  
  18. void (*zobraz[]) (pohyb *, const smer, const int) = {
  19.         zobraz_opilce1, zobraz_opilce2, zobraz_opilce3,
  20.         zobraz_opilce1, zobraz_opilce3
  21. };
  22.  
  23. void zobraz_opilce1(pohyb * p, const smer s, const int pocet)
  24. {
  25.     switch (s) {
  26.     case dopredu:
  27.         p->pozice++;
  28.         TISKNIOPILCER("->")
  29.             break;
  30.     case dozadu:
  31.         p->pozice--;
  32.         TISKNIOPILCER("<-")
  33.             break;
  34.     case stat:
  35.         TISKNIOPILCER("<>")
  36.             break;
  37.     };
  38. }
  39.  
  40. void zobraz_opilce2(pohyb * p, const smer s, const int pocet)
  41. {
  42.     switch (s) {
  43.     case dopredu:
  44.         p->pozice++;
  45.         TISKNIOPILCEN("->")
  46.             break;
  47.     case dozadu:
  48.         p->pozice--;
  49.         TISKNIOPILCEN("<-")
  50.             break;
  51.     default:
  52.         TISKNIOPILCEN("<>")
  53.             break;
  54.     };
  55. }
  56.  
  57. void zobraz_opilce3(pohyb * p, const smer s, const int pocet)
  58. {
  59.     switch (s) {
  60.     case dopredu:
  61.         p->pozice++;
  62.         TISKNIOPILCER(">>")
  63.             break;
  64.     case dozadu:
  65.         p->pozice--;
  66.         TISKNIOPILCER("<<")
  67.             break;
  68.     default:
  69.         TISKNIOPILCER("><")
  70.             break;
  71.     };
  72. }
  73.  
  74. void zobraz_opilce4(pohyb * p, const smer s, const int pocet)
  75. {
  76.     switch (s) {
  77.     case dopredu:
  78.         p->pozice++;
  79.         printf("\rPOZICE: %2i%*c", p->pozice++, DELKACHUZE+10, ' ');
  80.         break;
  81.     case dozadu:
  82.         p->pozice--;
  83.         printf("\rPOZICE: %2i%*c", p->pozice++, DELKACHUZE+10, ' ');
  84.         break;
  85.     default:
  86.         printf("\rPOZICE: %2i%*c", p->pozice++, DELKACHUZE+10, ' ');
  87.         break;
  88.     };
  89. }
  90.  
  91. /*------------------------------------------------*/

A konečně k srdci programu, souboru opakov1.c.

  1. /*------------------------------------------------*/
  2. /* c17/opakov1.c                                  */
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6.  
  7. #ifdef unix
  8.     #include "unix1.h"
  9.     #define VERZE "UNIX"
  10. #elif defined __MSDOS__
  11.     #define VERZE "MSDOS"
  12.     #include "dos1.h"
  13. #elif defined __WINDOWS__ || defined __WIN16__ || defined __WIN32__ || defined __WIN64__ || defined _MSC_VER
  14.     #include "windows1.h"
  15.     #define VERZE "WINDOWS"
  16. #endif
  17.  
  18. #define CEKEJ 100
  19.  
  20. #include "define1.h"
  21. #include "zobraz1.h"
  22.  
  23. /* Pokusim se nacist cisla z prikazove radky a pote zkontroluji,
  24.  * zda maji rozumne hodnoty. Pokud ne, program ukoncim */
  25.  
  26. pohyb nacti_procenta(int argc, char *argv[])
  27. {
  28.     pohyb p = { 0, 0, 0, DELKACHUZE / 2 };
  29.     if (argc != 3) {
  30.         printf("Spatny pocet argumentu\n"
  31.                "Zadejte pravdepodobnost chuze vpred a vzad.\n");
  32.         exit(0);
  33.     }
  34.     p.vpred = atoi(argv[1]);
  35.     p.vzad = atoi(argv[2]);
  36.     p.stop = 100 - p.vpred - p.vzad;
  37.  
  38.     if (p.stop < 0) {
  39.         printf("Chybne zadane pravdepodobnosti\n");
  40.         printf("Jejich soucet nesmi prekrocit 100\n");
  41.         exit(0);
  42.     }
  43.  
  44.     if (!(p.vpred || p.vzad)) {
  45.         printf("Pravdepodobnosti vpred i vzad musi byt nenulove\n");
  46.         exit(0);
  47.     }
  48.     if ((p.vpred < 0) || (p.vzad < 0)) {
  49.         printf("Pravdepodobnosti musi byt kladne!\n");
  50.         exit(0);
  51.     }
  52.     return p;
  53. }
  54.  
  55. /**
  56.  * nahodne vybere smer pohybu opilce
  57.  */
  58. smer vyber_smer(const pohyb * p)
  59. {
  60.     int x;
  61.     x = (rand() % 100);         /* tj. 0 - 99 */
  62.     if (x <= p->vpred)
  63.         return dopredu;
  64.     if (x <= p->vpred + p->vzad)
  65.         return dozadu;
  66.     return stat;
  67. }
  68.  
  69.  
  70. int main(int argc, char *argv[])
  71. {
  72.     pohyb opilec;
  73.     int pocet_funkci, fce, pocet;
  74.     opilec = nacti_procenta(argc, argv);
  75.     /* Diky tomu, ze nahodne cisla inicializuji "nenahodne", pak
  76.      * pri stejne zadanych procentech se bude program chovat stejne.
  77.      * Lepe je funkcisrand inicializovat treba na zaklade aktualniho
  78.      * casu */
  79.     srand(opilec.vpred * opilec.vzad);
  80.     /* NULL je ukazatel "do nikam". Jako takovy ma stejnou velikost
  81.      * jako kterykoliv ukazatel, vcetne ukazatele na funkci.
  82.      * Tj. sizeof(NULL) = sizeof(char *) atd. */
  83.     pocet_funkci = sizeof(zobraz) / sizeof(NULL);
  84.  
  85.     pocet = 0;
  86.     do {
  87.         pocet++;
  88.         /* vybiram nahodne funkci na zobrazeni */
  89.         fce = rand() % pocet_funkci;
  90.         zobraz[fce] (&opilec, vyber_smer(&opilec), pocet);
  91.         /* vyprazdneni standardniho vystupu pred pozastavenim */
  92.         fflush(stdout);
  93.         cekej(CEKEJ);
  94.     } while ((opilec.pozice > 2) && (opilec.pozice < DELKACHUZE-1));
  95.  
  96.     printf("\n");
  97.     return 0;
  98. }
  99.  
  100. /*------------------------------------------------*/

Možný průběh programu:

$ opakov1 30 50
 50% (              <<                       )  30% (  8)
 50% (               >>                      )  30% ( 17)
 50% (              <>                       )  30% ( 24)
 50% (           <<                          )  30% ( 27)
 50% (            ->                         )  30% ( 28)
 50% (           <<                          )  30% ( 38)
 50% (         <>                            )  30% ( 43)
 50% (         >>                            )  30% ( 45)
 50% (         <-                            )  30% ( 49)
 50% (        <-                             )  30% ( 52)
 50% (   <<                                  )  30% ( 64)
 50% (  <<                                   )  30% ( 70)
 50% (<-                                     )  30% ( 72)

1) const je zkratka pro const int, viz zobraz1.h

Komentář Hlášení chyby
Created: 29.8.2003
Last updated: 21.3.2015
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..