Vytváření typů II

V této kapitole vám povím o dalších podivuhodných konstrukcích jazyka C. Zde probírané konstrukce patří již k těm pokročilejším a jejich využití není tak časté, ale přesto se vyplatí je znát. Zvláště výčtový typ enum.

Výčtový typ enum

Typ enum vám umožní vytvořit proměnnou, která může obsahovat pouze hodnoty konstant určených při deklaraci výčtového typu. Proměnná se vytváří pomocí typedef.

Deklarace typu enum má hodně společného s deklarací struktury. Lze vytvořit buďto jen výčtový typ enum (stejně jako jenom strukturu), nebo pomocí typedef definovat nový datový typ. Protože je lepší a častější použití s typedef, ukáži jenom jednou syntaxi výčtového typu a dále v příkladech budu pracovat jen spolu s konstrukcí s typedef.

enum [jmeno] {
výčet konstant
...
} [promenne];

Příklad vytvoření výčtového typu:

enum kalendar {
 leden = 1, unor, brezen, duben, kveten, cerven, cervenec, srpen, zari,
 rijen, listopad, prosinec
} a, b;
...
a = unor;
...
if (b == duben) {
...

V příkladu jsem vytvořil výčtový typ se jménem kalendar a definoval dvě proměnné a a b. Konstanty výčtového typu jsou vždy celočíselné. Pokud konstantě nepřiřadíte hodnotu (jako leden=1), pak má hodnotu o jednotku vyšší, než konstanta předešlá (unor tedy odpovídá číslu 2, brezen 3 atd). Pokud nepřiřadíte hodnotu ani první konstantě, automaticky je jí přiřazena nula. Hodnoty lze přiřadit kterékoliv konstantě ve výčtovém typu. Opět platí, že následující konstanta, pokud nemá přiřazenou hodnotu, je o jednotku větší, než předcházející konstanta.

Překladač tyto konstanty chápe pouze jako čísla. Je tedy dost dobře možné je srovnávat s čísly (např if(leden == 1)), ale to by zcela postrádalo smysl. Výčtový typ se používá právě proto, aby se v programu nemusela konkrétní čísla používat. Použití výčtového typu se podobá makrům preprocesoru. Jeho výhodou je právě to, že lze vytvářet proměnné výčtového typu, čímž se dá program zpřehlednit a jeho čtení je pak o něco snazší. Taktéž překladač může odhalit všelijaké chyby (třeba když do proměnné výčtového typu přiřadíte číslo, které neodpovídá žádné konstantě z výčtového typu.

V následujícím příkladu je již vidět použití typu enum s konstrukcí typedef.

/*------------------------------------------------*/
/* c16/enum1.c                                    */

#include <stdio.h>

typedef enum {
    vlevo, vpravo, stred, center = stred
} zarovnani;


void tiskni_znak(char ch, zarovnani zr)
{
    switch (zr) {
    case vlevo:
        printf("%c\n", ch);
        break;
    case vpravo:
        printf("%50c\n", ch);
        break;
    case stred:
        printf("%25c\n", ch);
        break;
    default:
        printf("%c%24c%25c\n", '?', '?', '?');
    }
}

int main(void)
{
    tiskni_znak('A', 50);
    tiskni_znak('X', vlevo);
    tiskni_znak('Y', center);
    tiskni_znak('Z', vpravo);
    return 0;
}

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

Výstup z programu:


?                       ?                        ?
X
                        Y
                                                 Z

Typ union

Syntaxe typu union je stejná jako struktury, až na to, že místo klíčového slova struct se použije klíčové slovo union. Význam jednotlivých položek je stejný. Jeho použití s konstrukcí typedef také.

union [jmeno] {
     typ jmeno_polozky;
     typ jmeno_polozky;
     ...
} [promenne];

Rozdíl tu však je. A zásadní. Zatímco struktura si vytvoří paměťové místo pro všechny položky, typ union zabírá v paměti jen tolik místa, kolik největší jeho položka. Z toho také vyplývá, že lze používat v jeden okamžik jen jednu položku. Kterou, to už závisí na programátorovi. Každá položka začíná v začátku paměti unionu.

Typ union může při programování ušetřit paměť. Navíc můžeme vytvořit funkci, která bude vracet různé datové typy jako typ union (viz příklad níže). Prvkem v union může být i struktura, nebo jiný typ union atp.

Union se dá inicializovat takto (inicializuje se samozřejmě jen jedna položka):

union datovy_typ {
    char ch;
    unsigned int uin;
    float fl;
}
union datovy_typ cislo1 = { 'a' }; /* inicializuje ch */
union datovy_typ cislo2 = { uin: 5 }; /* inicializuje uin */
union datovy_typ cislo3 = { .uin = 5 }; /* inicializuje uin */

Funkce rand() a srand()

V příkladu vás seznámím, kromě použití výčtového typu enum a typu union, se dvěma novými funkcemi. Funkce rand() vrací pseudonáhodné číslo v rozmezí 0 až RAND_MAX. Pseudonáhodné proto, protože vrací čísla z číselné řady generované na základě nějakých matematických algoritmů.

Aby tato čísla nebyla při spuštění programu generována vždy stejně, nastaví se počátek této řady pomocí funkce srand(). Argumentem této funkce je číslo typu unsigned int. Dobré je předat této funkci jako argument aktuální čas. Protože pracovat s časem ještě neumíte, nastavím jej podle toho, co zadá uživatel programu. Budete-li jako uživatel zadávat stále stejné číslo, bude program vracet stále stejné výsledky. Příklad použití srand() s časem najdete až v kapitole věnující se standardnímu hlavičkovému souboru <time.h>.

Funkce rand() a srand() jsou definovány ve standardním hlavičkovém souboru <stdlib.h>.

  1. /*------------------------------------------------*/
  2. /* c16/union.c                                    */
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6.  
  7.     znak, cele, racionalni
  8. } typ;
  9.  
  10.     char ch;
  11.     unsigned int uin;
  12.     float fl;
  13. } datovy_typ;
  14.  
  15. datovy_typ nahodne_cislo(typ t)
  16. {
  17.     datovy_typ x;
  18.  
  19.     switch (t) {
  20.     case znak:
  21.         x.ch = (char) (rand() % ('z' - 'a')) + 'a';
  22.         break;
  23.     case cele:
  24.         x.uin = (unsigned int) rand();
  25.         break;
  26.     case racionalni:
  27.         x.fl = (float) rand() / RAND_MAX;
  28.         break;
  29.     }
  30.     return x;
  31. }
  32.  
  33. int main(void)
  34. {
  35.     datovy_typ un;
  36.  
  37.     printf("Zadej cislo: ");
  38.     if (scanf("%u", &un.uin) == 0)
  39.         return 1;
  40.  
  41.     srand(un.uin);
  42.     un = nahodne_cislo(racionalni);
  43.     printf("float = %f\n", un.fl);
  44.  
  45.     un = nahodne_cislo(znak);
  46.     printf("char = %c\n", un.ch);
  47.  
  48.     un = nahodne_cislo(cele);
  49.     printf("int = %i\n", un.uin);
  50.  
  51.     printf("velikost un = %lu == float = %lu bajty\n", sizeof(un), sizeof(float));
  52.     return 0;
  53. }
  54.  
  55. /*------------------------------------------------*/
Visual Studio

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

Možný výstup z programu:

Zadej cislo: 123
float = 0.060051
char = n
int = 436085873
velikost un = 4 == float = 4 bajty

Typ union se moc často nepoužívá. Šetření pamětí už není dneska in a přednost se dává psaní srozumitelnějších programů. Lepší řešení předchozího příkladu by bylo napsat 3 funkce pro získání náhodného čísla (pro znak, unsigned int a float). Nepotřebovali byste ani enum, ani union a měli byste 3 krátké, čitelné funkce místo jedné dlouhé nepřehledné.

Typ union se s výhodou používá také jako argument funkcí, které potřebují pro různé situace různé vstupní datové typy, ale vždy právě jen jeden z nich. Ale i pro tyto situace platí předchozí poznámka - raději se unionu vyhýbejte, pokud k němu nemáte opravdu dobrý důvod.

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