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
.
Příklad vytvoření výčtového typu:
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é.
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):
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>.
- /*------------------------------------------------*/
- /* c16/union.c */
- #define _CRT_SECURE_NO_WARNINGS
- #include <stdio.h>
- #include <stdlib.h>
- znak, cele, racionalni
- } typ;
- char ch;
- float fl;
- } datovy_typ;
- datovy_typ nahodne_cislo(typ t)
- {
- datovy_typ x;
- case znak:
- case cele:
- case racionalni:
- }
- return x;
- }
- {
- datovy_typ un;
- un = nahodne_cislo(racionalni);
- un = nahodne_cislo(znak);
- un = nahodne_cislo(cele);
- }
- /*------------------------------------------------*/
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.