7. přednáška http://www.uai.fme.vutbr.cz/~vdumek/ 20. 3. 2008 - práce se strukturami - bitové operace - bitová pole - uniony (sjednocení) Studijní materiály najdete na adrese: http://www.uai.fme.vutbr.cz/~vdumek/
Struktury - oba způsoby lze libovolně kombinovat struct { struct STRU { int k1, k2; float h, z; char *buf1; char pole[10]; } to_to; }; - oba způsoby lze libovolně kombinovat struct STRU s1, s2, s3; struct STRU { float h, z; char pole[10]; } a, b, c; - struktury lze při deklaraci inicializovat (podle K&R pouze externí a statické) { deklarace struktury} = { seznam inicializatoru };
Struktury struct { int a, b; char *c; int pole[5]; double d; } upoc = { 1, 2, “TEXT”, {1, 3, 5, 7, 9}, 3.141312 }; typedef struct { char jmeno[25]; cgar trida; short podtrida; float dekl, rekt, vzdal; } hvezda; main() { hvezda moje_hvezda; strcpy(moje_hvezda.jmeno, “epsilon eridani”); moje_hvezda.trida = ‘K‘; moje_hvezda.podtrida = 4 ….. }
Příklady struktur struct { int vyska; float vaha; } pavel, honza, karel; struct miry { int vyska; float vaha; } pavel, honza, karel; typedef struct { int vyska; float vaha; } MIRY; MIRY pavel, honza, karel; struct miry { int vyska; float vaha; }; struct miry pavel; struct miry honza, karel; pozor, nelze !!! miry pavel, honza, karel; typedef struct miry{ int vyska; float vaha; } MIRY; MIRY pavel, honza, karel;
Struktury - jméno struktury je nepovinné, slouží pouze jako identifikátor šablony - je-li jméno nadefinováno, používáme je k pozdější deklaraci - nejsou-li při deklaraci specifikovány proměnné typu struktura, vytvoří se jen šablona se jménem - proměnné strukturovaných typů, které nemají jmenovku, nemůžeme předávat jako parametr funkce. - strukturu můžeme i inicializovat - do složených závorek napíšeme seznam hodnot proměnných obsažených ve struktuře - nemusíme inicializovat všechny prvky, lze vynechat prvky na konci struktury - struktury lze navzájem vnořovat (struktura nemůže obsahovat proměnnou svého vlastního typu)
Práce se strukturami - stejně jako u proměnných máme možnost deklarovat směrník na strukturu, je pro něj zaveden operátor ->, přináší možnost řetězit struktury, členem struktury nesmí být struktura stejného typu, přípustný je pouze směrník struct MOJE { float a, b; int c; char *smer; }; struct MOJE da_da; struct MOJE *sm_moje; …….. sm_moje = calloc(1, sizeof(struct MOJE)); struct TADY { struct TADY { ….. …… struct TADY a; struct TADY *a; …… ……. }; }; NE ANO
Ukazatel na strukturu - ukazatel na strukturu typedef struct { char jmeno[30]; int rocnik; } STUDENT; STUDENT s, *p_s; - alokování paměti: p_s = (STUDENT *) malloc(sizeof(STUDENT)); - inicializace směrníku: p_s = &s; - definice typu směrník na strukturu: } STUDENT, *P_STUDENT; STUDENT s; P_STUDENT p_s; p_s = (P_STUDENT) malloc(sizeof(STUDENT));
Struktury - přístup k prvkům struktury, operátor . zpřístupňuje proměnné ze struktury - operátor -> zpřístupňuje proměnné ze struktury, na kterou máme ukazatel pavel.vyska = 190; pavel.vaha = karel.vaha; struct miry *p_karel; p_karel = &karel; p_karel->vyska = 184; (*p_karel).vyska = 184; často používáme pole struktur, nebo dynamické datové typy vytvořené jako spojové seznamy ze struktur - například binární strom: struct tree { struct tree *left; /* struct tree { struct tree b; … */ struct tree *right; } a;
Práce se strukturami - zpřístupnění prvku struktury se provádí přes proměnnou, nebo přes směrník da_da.a = 5.46; da_da.c = 3; da_da.b = da_da.a / da_da.c; sm_moje->a = 6.78; sm_moje->c = 7; sm_moje->b = sm_moje->a / sm_moje->c; sm_moje->smer = calloc(100, sizeof(char)); - další operace pro struktury jsou: získání adresy, přiřazení struktur stejného typu, parametr, návratová hodnota funkce - pro získání adresy se používá operátor &, deklarovaný směrník musí být generický nebo na strukturu stejného typu - jako parametr i návratová hodnota mohou být i směrníky na strukturu
Práce se strukturami
Práce se strukturami struct XXX { p=&v; /* ziskani adresy */ int a, b, c; x=v; /* prirazeni struktur */ float d; } v, x, *y; struct XXX fce(struct XXX m) { struct XXX z; struct XXX *p; ….. return z; } - v dalším příkladu nejsou struktury a, m, n formálně shodné, nel- ze je proto přiřazovat struct SSSR { struct { float b, f; float b, f; }; } a; struct SSSR m, n; - kompilátory pro normu ANSI mají možnost nastavit zarovnávání ukládání struktury na hranici slova, každá struktura potom začí- ná na sudé adrese, každý člen struktury jiného typu než char bude mít sudou relativní adresu vzhledem k začátku struktury
Práce se strukturami struct person { struct date { char name[NAMESIZE]; char addr[ADDRSIZE]; long zipcode; double salary; struct date birthdate; struct date hiredate; }; struct date { int day; int month; int year; int yesterday; char mon_name[4]; }; … struct person KAREL; KAREL.birthdate.month /* odkaz na měsíc narození */ /* operátor kvalifikace se sdružuje*/ /* zleva doprava*/
Práce se strukturami struct { /* ++p->x inkrementuje x, nikoliv p, neboť */ int x; /* priorita zajistí provedení ++(p->x) */ int y; } *p; /* pro jiné vyhodnocení musíme změnit prioritu, */ /* (++p)->x inkrementace před dosažením x */ /* (p++)->x inkrementace po dosažení x */ struct { int *x; int *y; } *m; *m->y obsah místa, kam ukazuje y *m->x++ inkrementuje x po přístupu k místu, kam ukazuje (*p->y)++ inkrementuje obsah místa, kam ukazuje y p++->x inkrementuje p po přístupu k místu, kam ukazuje x
#include <stdio.h> #include <string.h> #define ZNAKU_NAZEV 25 #define POLOZEK_ZBOZI 10 #define FORMAT_VYROBEK "cislo:%5d pocet:%5d cena:%10.2f nazev:%s\n" typedef struct {float re, im;} complex; typedef struct { int ev_cislo; char nazev[ZNAKU_NAZEV + 1]; int na_sklade; float cena; } vyrobek; typedef vyrobek zbozi[POLOZEK_ZBOZI]; int main(void) { complex cislo, im_jednotka = {0, 1}; zbozi polozky; vyrobek *ppolozky, a = {8765, "nazev zbozi na sklade", 100, 123.99}; cislo.re = 12.3456; cislo.im = -987.654; polozky[0].ev_cislo = 0; strcpy(polozky[0].nazev, "polozka cislo 0"); polozky[0].na_sklade = 20; polozky[0].cena = 45.15;
ppolozky = polozky + 1; ppolozky->ev_cislo = 1; /* (*ppolozky).ev_cislo = 1; */ strcpy(ppolozky->nazev, "polozka cislo 1"); ppolozky->na_sklade = 123; ppolozky->cena = 9945.15; printf("re = %10.5f im = %10.5f\n", im_jednotka.re, im_jednotka.im); printf("re = %10.5f im = %10.5f\n", cislo.re, cislo.im); printf(FORMAT_VYROBEK, a.ev_cislo, a.na_sklade, a.cena, a.nazev); printf(FORMAT_VYROBEK, polozky[0].ev_cislo, polozky[0].na_sklade, polozky[0].cena, polozky[0].nazev); printf(FORMAT_VYROBEK, ppolozky->ev_cislo, ppolozky->na_sklade, ppolozky->cena, ppolozky->nazev); return 0; } Výstup získaný spuštěním programu: re = 0.00000 im = 1.00000 re = 12.34560 im = -987.65399 cislo: 8765 pocet: 100 cena: 123.99 nazev:nazev zbozi na sklade cislo: 0 pocet: 20 cena: 45.15 nazev:polozka cislo 0 cislo: 1 pocet: 123 cena: 9945.15 nazev:polozka cislo 1
Bitové operace & bitový součin | bitový součet ^ bitový exklusivní součet << posun doleva >> posun doprava ~ jedničkový doplněk
Bitová pole - pokud potřebujeme do jednoho slova uložit více než jeden objekt, používáme bitová pole, bitové pole je množina sousedních bitů v rámci proměnné typu int deklarace a použití takových proměnných je stejné jako u struktur, pouze u jednotlivých proměnných používáme operátor : pro stanovení počtu jednotlivých bitů struct flags { unsigned a:1; :2; /* zarovnání */ unsigned b:1; unsigned c:3; } flag; flag.a = 1; /* použití */ vynecháme-li jméno bitového pole, použijeme vyhrazené bity pouze jako výplň pro zarovnání specifická délka 0 se používá pro zarovnání na slovo bitové pole musí být typu int, signed int nebo unsigned int jednotlivé proměnné uvnitř bitového pole nemají své adresy bitová pole většinou nevedou k ušetření paměti (více instrukcí, ...)
Bitová pole bitový součin - & 77 & 198 = 68 1001101 & 11000110 = 01000100 Operandy operátoru binárního součinu mohou být pouze výrazy celočíselného typu, není tedy možné provádět bitový součin s hodnotami typu float, double ani long double. #define liche(x) (1 & (x)) makro pro určení lichosti a sudosti bitový součet - | 77 | 198 = 207 1001101 | 11000110 = 11001111 bitový exklusivní součet - ^ 77 ^ 198 = 139 1001101 ^ 11000110 = 10001011 bitová negace - ~ Na rozdíl od předchozích operátorů je bitová negace unárním operátorem, tedy má pouze jeden operand. Ten musí být, stejně jako v předchozích případech, celočíselného typu. Při provádění operace jsou postupně všechny jeho bity negovány a takto získaná hodnota je výsledkem použití operátoru. V následujícím příkladu uvažujeme typy unsigned char. V případě použití typu signed by ještě hrál roli negovaný znaménkový bit.
Bitová pole ~77 = 178 01001101 10110010 Protože jsou negovány všechny bity čísla, včetně počátečních nul, znamená to, že pro operandy se stejnou hodnotou, ale různým typem (char, short, int, long int), bude výsledná hodnota různá. bitový posun doleva - << Při posunu se bity nejvíce nalevo ztrácejí a zprava jsou uvolněná místa doplněna nulami. Následující příkaz vrátí o dvě místa bitově posunutou hodnotu čísla 45. 45 << 2; před posunem (hodnota 45) 00101101po posunu (hodnota 180, výsledek celého výrazu) 10110100 Bitový posun doleva se často používá k provádění násobení mocninami dvou. Tento způsob je totiž znatelně rychlejší než normální násobení. Platí, že x << n = x*2n. Schopnější překladače dnes již samy nahrazují obyčejné násobení bitovým posunem. bitový posun doprava - >> Komplementární operací k bitovému posunu doleva je bitový posun doprava. V tomto případě se bity zprava ztrácí a zleva jsou doplňovány nulou u neznaménkových typů, nebo znaménkovým bitem u typů znaménkových, což je ale implementačně závislé. Stejně jako se dal bitový posun doleva použít k násobení, lze zase bitový posun doprava použít k celočíselnému dělení mocninami dvou. x >> n = x/2n 48 >> 4 = 3
Bitová pole - jde o zvláštní případ členu struktury, může být znamémkové, bezznaménkové, implicitní chápání bezznaménkovosti závisí na impementaci kompilátoru, je tedy lepší explicitně určit typ signed nebo unsigned - délka bitového pole může být 1 až 16 bitů, nevyužité bity zůstávají jako rezerva, nevztahuje se na ně zarovnávání na hranici slova - bitové pole může být pouze členem struktury, struktura s bitovým polem může obsahovat i členy běžného typu typ [identifikator] : sirka; typ - char, unsigned char, int, unsigned int identifikator - jméno pro skupinu bitů, pokud je vynechán, jsou bity rezervovány sirka - pocet bitů pro danou skupinu
Bitová pole - hodnoty jsou ukládány ve dvojkovém doplňkovém kódu s inter- pretací závislou na znaménkovosti struct POLE_BITU { Dosažitelnost prvků int i : 2; my_my.i = 1; unsigned j : 5; my_my.j = 23; int : 4; my_my.k = 0; int k : 1; my_my.m = -1; int m : 4; } my_my; 15 14 13 12 11 3 4 10 6 9 8 7 5 2 1 - -1 -- 23 m k nepoužito j i - používání bitových polí může přinášet problémy s portabilitou programů, detaily ukládání jsou implementačně závislé - nelze získat adresu bitového pole
Bitová pole #define KEYWORD 01 #define EXTERNAL 02 #define STATIC 04 ... flags |= EXTERNAL | STATIC; /* nastaví bity EXTERNAL a STATIC */ flags &= ~(EXTERNAL | STATIC); /* vynuluje bity EXTERNAL a STATIC */ if((flags & (EXTERNAL | STATIC)) == 0 … /* splněná podmínka pro vynulované */ struct { unsigned KEYWORD : 1; unsigned EXTERNAL : 1; unsigned STATIC : 1; } flags; flags.EXTERN = flags.STATIC = 1; /* nastavení bitů */ flags.EXTERN = flags.STATIC = 0; /* vynulování bitů*/ if(flags.EXTERN == 0 && flags.STATIC == 0) ... /* testování */
Uniony - union je datový typ, který má několik prvků různých typů, ale programátor může používat vždy jen jeden z nich, tyto prvky leží v paměti na jednom místě, velikost unionu odpovídá velikosti největšího prvku - union šetří místo v paměti, pokud programátor ví, že bude potřebovat pouze jeden prvek, ale neví, jakého typu, při nepovinné inicializaci uvádíme ve {} hodnotu příslušnou prvnímu typu - přístup k prvkům unionu, operace s uniony jsou stejné jako u struktur, uniony mohou být součástí polí a struktur a opačně union jmeno { int ival; float fval; char *pval; } a; - pokud chceme šablonu použít později: union jmeno
Uniony - sjednocení, slouží k ukládání složek různého datového typu na jedno místo paměti, složky netvoří celou proměnnou, v jednom okamžiku je proměnná tvořena vždy jedním ze členů. K jednomu paměťovému místu tak lze přistupovat různým způsobem struktura: char + int + float = 7 B union: char + int + float = 4 B - deklarace unionu má tvar: union jmenovka {<seznam slozek> } promenna; - význam jednotlivých deklarátorů je shodný se strukturami
Uniony - bitové pole už smí být členem unionu, všechny členy unionu se ukládají od stejné adresy a překrývají se, velikost unionu je tedy dána velikostí největší složky - opět není povoleno vkládání unionů stejného typu do sebe, opět se řeší pomocí směrníku promenna_union.slozka smernik_union -> slozka union znamenko { long znam; /* 4 B, n.znam */ unsigned long bez_znam; /* 4 B, n.bez_znam */ } n; struct { char jmeno[30]; enum {muz, zena} pohlavi; union { enum {ne, ano} vojak; /* zamestnanec.zdata.vojak */ char rozena[20]; } zdata; /* zamestnanec.zdata.rozena */ } zamestnanec;
Uniony
Uniony - použití unionu pro simulaci registrů procesoru (DOS.H) struct BYTEREGS { unsigned char al, ah, bl, bh; unsigned char cl, ch, dl, dh; }; struct WORDREGS { unsigned int ax, bx, cx, dx; unsigned int si, di, cflag, flags; union REGS { struct WORDREGS x; struct BYTEREGS h; void main(void) { union REGS TAMARA; ….. TAMARA.h.al = 0x34; TAMARA.h.ah = 0x12; printf(“0x%x”, TAMARA.x.ax); /* vytiskne se 0x1234 */ }
Adresa struktury: 913E:0004 Hodnota y: 11 Adresa struktury: 913E:0008 struct TEST { int x; int y; } *p, *p1; p=(struct TEST*)calloc(1, sizeof(struct TEST)); p1=p; p->x=22; p->y=11; printf("\nAdresa struktury: %p", p); printf("\nHodnota y: %d", p->y); p++->y; p=p1; (--p)->y; p->y++; printf("\nHodnota y: %d", (p->y)++); (p->y)++; printf("\nHodnota y: %d", ++(p->y)); Adresa struktury: 913E:0004 Hodnota y: 11 Adresa struktury: 913E:0008 Hodnota y: 28531 Adresa struktury: 913E:0000 Hodnota y: -28387 Hodnota y: 12 Hodnota y: 15