8. díl - Výčtové typy

C++ Pokročilé konstrukce Výčtové typy

V minulém článku jsme si řekli o binárních operacích. Zmínil jsem tzv. bitové flagy (nebo také příznaky), na které aplikujeme binární operace. Bylo by nepraktické pamatovat si jednotlivé čísla (ještě k tomu v binární podobě). Jednodušší by bylo si jednotlivé hodnoty pojmenovat a pracovat pouze se symbolickými názvy. To je úkol výčtových typů.

Výčtový typ

Jako ukázku výčtového typu mohou být například měsíce nebo dny v týdnu. Jedno z nejzákladnějších řešení by bylo nadefinovat si celé číslo a říct si, že hodnota 0 bude reprezentovat leden, hodnota 1 únor atd. Toto řešení bude funkční a lze jej v programu použít, ale musíme si (my líní programátoři) pamatovat, který měsíc má jaké číslo. Navíc tento přístup brání IDE k inteligentnímu napovídání a kontrole, zda jsme skutečně napsali to, co jsme napsat chtěli.

Z toho vznikl požadavek pamatovat si namísto hodnot názvy. Určitě vás hned napadne několik způsobů, například nadefinovat si několik konstant. Stejně dobře by mohla posloužit globální struktura s předdefinovanými hodnotami. Céčko tento požadavek řeší ještě mnohem lépe - výčtovými typy. Ty jsou stále interně reprezentovány jako celé číslo, ale namísto hodnoty používáme v programu jméno. Výčtový typ se deklaruje pomocí klíčového slova enum.

typedef enum
{
    MESIC_LEDEN,
    MESIC_UNOR,
    MESIC_BREZEN,
    MESIC_DUBEN
    // ....
} MESICE;

V programu poté můžeme pracovat s názvy namísto s hodnotami:

void zobraz_mesic(MESICE mesic)
{
    switch(mesic)
    {
        case MESIC_LEDEN:
            printf("Dnes je leden\n");
            break;
        case MESIC_UNOR:
            printf("Dnes je unor\n");
            break;
        case MESIC_BREZEN:
            printf("Dnes je brezen\n");
            break;
        case MESIC_DUBEN:
            printf("Dnes je duben");
            break;
    }
}

int main(int argc, char** argv)
{
    MESICE mesic = MESIC_UNOR;
    zobraz_mesic(mesic);
    return (EXIT_SUCCESS);
}
Ukázka výčtových typů v C

Výčtové typy jsou pořád interně reprezentovány jako celé číslo (unsigned int). Lze je tedy na celé číslo přetypovat. Standardně má první člen hodnotu 0 a každý následující člen má hodnotu o jednu větší. To ukazuje následující program:

int main(int argc, char** argv)
{
    printf("Hodnota ledna: %d\n", (int) MESIC_LEDEN);
    printf("Hodnota unoru: %d\n", (int) MESIC_UNOR);
    printf("Hodnota brezna: %d\n", (int) MESIC_BREZEN);
    printf("Hodnota dubna: %d\n", (int) MESIC_DUBEN);
    return (EXIT_SUCCESS);
}

Výstup programu:

Hodnota výčtového typu

Hodnoty pro jednotlivé názvy můžeme samozřejmě upravit. Pokud hodnotu názvu nepřiřadíme, má standardně hodnotu o jednu vyšší, než má předchozí název. Za zmínku také stojí, že dva názvy mohou mít stejnou hodnotu. Předchozí program přepíšeme:

typedef enum
{
    MESIC_LEDEN = 2,
    MESIC_UNOR,
    MESIC_BREZEN = 5,
    MESIC_DUBEN = 5
    // ....
} MESICE;

int main(int argc, char** argv)
{
    printf("Hodnota ledna: %d\n", (int) MESIC_LEDEN);
    printf("Hodnota unoru: %d\n", (int) MESIC_UNOR);
    printf("Hodnota brezna: %d\n", (int) MESIC_BREZEN);
    printf("Hodnota dubna: %d\n", (int) MESIC_DUBEN);
    return (EXIT_SUCCESS);
}
Úprava hodnot pro výštový typ

Bitové příznaky

Někdy nám stačí informace pouze ANO/NE. Protože má C nejmenší datový typ velikost 1 bajt - je to zbytečné plýtvání místem. Pro ANO/NE nám stačí pouze 1 bit! Zároveň máme zpravidla nějakou sadu informací, které spolu souvisí a které potřebujeme uchovat. V takových situacích je praktické použít bitové příznaky (nebo také flagy). Princip je takový, že pro celé číslo navážeme každému jednotlivému bitu nějakou informaci. Pomocí binárních operací následně můžeme jednotlivé informace extrahovat. Nejlépe opět poslouží příklad. Nejdříve si vytvoříme bitové příznaky dnů. Následně se ve funkci podíváme na každý den a zhodnotíme, jestli se ten den pracuje.

typedef enum
{
    DEN_PONDELI = 0x1,
    DEN_UTERY = 0x2,
    DEN_STREDA = 0x4,
    DEN_CTVRTEK = 0x8,
    DEN_PATEK = 0x10,
    DEN_SOBOTA = 0x20,
    DEN_NEDELE = 0x40
} DNY;

void pracovni_dny(int dny)
{
    if (dny & DEN_PONDELI)
        printf("Pracuje se v pondeli\n");
    if (dny & DEN_UTERY)
        printf("Pracuje se v utery\n");
    if (dny & DEN_STREDA)
        printf("Pracuje se ve stredu\n");
    if (dny & DEN_CTVRTEK)
        printf("Pracuje se ve ctvrtek\n");
    if (dny & DEN_PATEK)
        printf("Pracuje se v patek\n");
    if (dny & DEN_SOBOTA)
        printf("Pracuje se v sobotu\n");
    if (dny & DEN_NEDELE)
        printf("Pracuje se v nedeli\n");
}

int main(int argc, char** argv)
{
    pracovni_dny(DEN_PONDELI | DEN_UTERY | DEN_STREDA | DEN_CTVRTEK | DEN_PATEK);
    return (EXIT_SUCCESS);
}

Je praktické uvádět výčtové typy v šestnáctkové soustavě, která má ke dvojkové blíže než desítková. Za povšimnutí stojí parametr funkce, který je typu int. Díky tomu, že jsou výčtové typy interně reprezentovány jako celočíselné hodnoty, není problém je na typ int přetypovat. Na závěr ještě přidávám výstup programu:

Použití bitových flagů v C

Union

Uniony se deklarují velmi podobně jako struktury, ale mají úplně jinou funkci. Řekněme, že chceme do programu opakovaně přenést nějakou informaci, ale tato informace může být vždy jiného typu. Můžeme to demonstrovat na příklad událostí: událost o pohybu myši ponese informaci, kam se myš posunula, zatímco při události stisku klávesy nás zajímá, která klávesa byla stisknuta. Tyto dvě struktury musíme nějakým způsobem předat programu. Udržovat obě struktury v paměti je náročné (představme si, že by událostí byly stovky). Proto C definuje datový typ union, který se může skládat z několika "elementů". Na rozdíl od klasických struktur, uvnitř unionu může být vždy pouze jeden z jeho elementů a celková velikost unionu se rovná velikosti největší elementu, kterou uchovává. Pro představu přidávám ukázku:

typedef struct
{
    int prvni_cislo;
    int druhe_cislo;
} STRUKTURA;

typedef union
{
    char znak;
    int cislo;
    STRUKTURA struktura;
} VLASTNI_TYP;

int main(int argc, char** argv)
{
    printf("Velikost typu: %d\n", sizeof (VLASTNI_TYP));

    VLASTNI_TYP promenna;
    promenna.cislo = 14;
    printf("Ulozeno: %d\n", promenna.cislo);

    promenna.struktura.prvni_cislo = 56;
    promenna.struktura.druhe_cislo = 4;
    promenna.znak = 'a'; //nyni jiz struktura neni validni
    printf("Ulozene cislo nyni (nedefinovane chovani): %d\n", promenna.cislo);

    return (EXIT_SUCCESS);
}
Ukázka unionu v C

Velikost unionu je 8 bajtů, protože jeho největší složkou je struktura, která má 8 bajtů (dvě 4 bajtová čísla). K jednotlivým prvkům přistupujeme stejně jako by se jednalo o strukturu. Je ale důležité si uvědomit, že přepsání jedné hodnoty zruší původně zapsanou hodnotu (jak to ukazuje poslední výpis). Prakticky tedy potřebujeme uchovávat vedle samotných dat i informaci o tom, jakou informaci union aktuálně obsahuje. Proto se stalo zvykem využívat union uvnitř struktury, které nese informaci o typu. Náš příklad s událostmi by vypadal nějak takhle:

typedef enum
{
    MYS,
    KLAVESNICE
} TYP_UDALOSTI;

typedef struct
{
    int posun_x;
    int posun_y;
    int pozice_x;
    int pozice_y;
} UDALOST_MYSI;

typedef struct
{
    char napsany_znak;
} UDALOST_KLAVESNICE;

typedef union
{
    UDALOST_MYSI mys;
    UDALOST_KLAVESNICE klavesnice;
} UNION_UDALOSTI;

typedef struct
{
    TYP_UDALOSTI typ;
    UNION_UDALOSTI udalost;
} UDALOST;

void zpracovani_udalosti(UDALOST udalost)
{
    if (udalost.typ == MYS)
    {
        //prekresli ukazatel na pozici
        //udalost.udalost.mys.pozice_x / udalost.udalost.mys.pozice_y
    }
    else if (udalost.typ == KLAVESNICE)
    {
        //napis znak udalost.udalost.klavesnice.napsany_znak
    }
}

Tento způsob se používá například v knihovně SDL. Pro dnešní díl je to vše a příště se podíváme na na operace s jednotlivými typy proměnných.


 

Stáhnout

Staženo 0x (n/a)
Aplikace je včetně zdrojových kódů v jazyce c

 

  Aktivity (2)

Článek pro vás napsal patrik.valkovic
Avatar
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu.

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 


Miniatura
Předchozí článek
Binární operace
Miniatura
Následující článek
Přetypování a operátory

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!