Struktury - složený datový typ. - datový typ jehož položky jsou jiné proměnné: - používá obvykle dohromady s příkazem typedef nechci vypisovat opakovaně složitou deklaraci pomocí typedef udělám nový datový typ: 9 Např.: // v hlavičkovém souboru nebo na začátku // programu (pod include): typedef struct char jmeno[20]; char prijmeni[20]; int rok_nar; CLOVEK; // Potom v programu CLOVEK a, b; a.rok_nar = 1995; strcpy(a.jmeno, "Pepa"); strcpy(a.prijmeni, "Novak"); printf("%s %s rok narozeni %d", a.jmeno, a.prijmeni, a.rok_nar); b.rok_nar = 1990; strcpy(b.jmeno, "Kunhuta"); strcpy(b.prijmeni, "Vonaskova"); printf("%s %s rok narozeni %d", b.jmeno, b.prijmeni, b.rok_nar); CLOVEK lidi[50]; lidi[0].rok_nar = 1993; strcpy(lidi[0].jmeno, "Eman"); strcpy(lidi[0].prijmeni, "Fiala");
printf("%s %s rok narozeni %d", lidi[0].jmeno, lidi[0].prijmeni, lidi[0].rok_nar); CLOVEK *p_lidi; int delka = 100; p_lidi = (CLOVEK *) malloc(delka * sizeof(clovek)); p_lidi[1].rok_nar = 1999; // jiné způsoby viz níže (p_lidi + 1)->rok_nar = 1999; // totez jako predch.r. free(p_lidi); Také lze: p_lidi[1].rok_nar = 1999; *(p_lidi + 1).rok_nar = 1999; (p_lidi + 1)->rok_nar = 1999; Méně často: typedef struct clovek char jmeno[20]; char prijmeni[20]; int rok_nar; ; struct clovek a, b, c;
Nebo: typedef struct clovek char jmeno[20]; char prijmeni[20]; int rok_nar; struct clovek *p_dite; CLOVEK; void main(void) CLOVEK a, b; CLOVEK lidi[50]; CLOVEK *p_clobrda; CLOVEK *p_lidi; strcpy(a.jmeno, "Pepa"); strcpy(a.prijmeni, "Novak"); a.rok_nar = 1971; strcpy(b.jmeno, "Anicka"); strcpy(b.prijmeni, "Vyskocova"); b.rok_nar = 1988; strcpy(lidi[0].jmeno, "Jana"); strcpy(lidi[0].prijmeni, "Novakova"); lidi[0].rok_nar = 1986; strcpy(lidi[1].jmeno, "Eliska"); strcpy(lidi[1].prijmeni, "Chobotova"); lidi[1].rok_nar = 1986; /* zde asi cyklus... */ /* A nyní dynamicky */ p_clobrda = (CLOVEK *) malloc(sizeof(clovek));
strcpy(p_clobrda->jmeno, "Helena"); strcpy(p_clobrda->prijmeni, "Vesela"); p_clobrda->rok_nar = 1987; lze i s tečkou: (*p_clobrda).rok_nar = 1987; /* A nyní dynamické pole lidí */ p_lidi = (CLOVEK *) malloc(100 * sizeof(clovek)); strcpy(p_lidi[0].jmeno, "Hermína"); strcpy(p_lidi[0].prijmeni, "Dávná"); p_lidi[0].rok_nar = 1910; strcpy((p_lidi + 1)->jmeno, "Lojza"); strcpy((p_lidi + 1)->prijmeni, "Cipera"); (p_lidi + 1)->rok_nar = 2010; free((void *) p_clobrda); free((void *) p_lidi); Nebo další příklad: typedef struct datum int den; int mesic; int rok; DATUM; typedef struct clovek char jmeno[20]; char prijmeni[20]; DATUM datum_narozeni; CLOVEK;
void main(void) CLOVEK a; // tady nějaký kód programu strcpy(a.jmeno, "Pepa"); strcpy(a.prijmeni, "Novak"); a.datum_narozeni.den = 20; a.datum_narozeni.mesic = 6; a.datum_narozeni.rok = 1971; Nebo: void main(void) CLOVEK *p_a; // tady nějaký kód programu p_a = (CLOVEK *) malloc(sizeof(clovek)) strcpy(p_a->jmeno, "Pepa"); strcpy(p_a->prijmeni, "Novak"); p_a->datum_narozeni.den = 20; p_a->datum_narozeni.mesic = 6; p_a->datum_narozeni.rok = 1971; ----------------------------- konec 12.4.2016 --------------
Pro vytvoření řetězu struktur (spojový seznam, linked-list): typedef struct clovek struct clovek *p_dalsi; char jmeno[20]; char prijmeni[20]; DATUM datum_narozeni; CLOVEK; void main(void) CLOVEK *p_lidi; p_lidi = (CLOVEK *) malloc(sizeof(clovek)); strcpy(p_lidi->jmeno, "Helena"); strcpy(p_lidi->prijmeni, "Vesela"); atd pro DATUM... p_lidi->p_dalsi = (CLOVEK *)malloc(sizeof(clovek));
Podobné jsou uniony typedef union char c; pouzivam bud to c nebo i int i; MUJ_UNION; MUJ_UNION prom; Lze použít: prom.c = 'A'; NEBO prom.i = 1234; /* tím jsem přepsal to 'A' */ Tj. v unionech jsou položky v paměti přes sebe. český název je také variantní záznam. Praktické použití typedef union double rozchod; // pro auto nebo: int typprednibrzdy; // pro motorku typy 1,2,3 PARAMETRY; typedef struct data char nazev[20]; int cislotypustroje; // 0 auto, 1 motorka double rozvor; PARAMETRY param; DOPRAV_PROSTR; Pro auto a motorku: DOPRAV_PROSTR jezditka[10]; // auto strcpy(jezditka[0].nazev, "BMW"); jezditka[0].cislotypustroje = 0; jezditka[0].rozvor = 2500.0; // mm jezditka[0].param.rozchod = 2000.0; // mm
// motorka strcpy(jezditka[1].nazev, "Harley"); jezditka[1].cislotypustroje = 1; jezditka[1].rozvor = 1500; // mm jezditka[1].param.typprednibrzdy = 3; // cislo typu // tisk parametru for (i = 0; i < kolikjezditek; i++) printf("nazev: %s\n", jezditka[i].nazev); printf("typ: %d\n", jezditka[i].cislotypustroje); printf("rozvor: %f\n", jezditka[i].rozvor); if (jezditka[i].cislotypustroje == 0) // je to auto printf("nazev: %f", jezditka[i].param.rozchod); else // je to motorka printf("typ brzd: %d", jezditka[i].param. typprednibrzdy);
Výčtový typ - typ obsahující hodnoty, které si zvolíme např. barvy, dny v týdnu atp. - je to ordinální typ tj. hodnoty jsou vnitřně číslovány dle pořadí zleva doprava (od nuly) typedef enum MODRA, CERVENA, ZELENA, ZLUTA BARVY; ordinální značí že MODRA je vnitřně 0, CERVENA 1. Tj. jdou porovnat. MODRA < CERVENA (něco jako porovnáván u znaků) Použití: BARVY a, b; a = MODRA; b = ZLUTA; - pozor: nelze je přímo tisknout!!! printf("barva je %d", b); vytiskne: 3 Pozor lze i toto: a = 30; // jde to i když logicky je to nesmysl stejně tak: a = ZELENA; a++; // v a bude ZLUTA a++; // v a bude 4, není barva, ale C to nevadi
- pokud má tisknout názvy barev: switch (b) case MODRA: printf("barva modra."); break; case CERVENA: printf("barva cervena."); break; case ZELENA: printf("barva zelena."); break; case ZLUTA: printf("barva zluta."); break; default: printf("neznama barva."); break; Nebo ne jako enum, ale jako pole řetězců: char *nazvybarev[] = "Modra", "Cervena", "Zelena", "Zluta"; printf("barva %s\n", nazvybarev[b]);
Použití pole uvnitř struktury využíté jako chytrá náhrada jednoduchého 1D pole: #include <stdio.h> #include <stdlib.h> typedef struct int *p_pole; int delkapole; LUXUSNI_NAHRADA_POLE_INT; void tisk(luxusni_nahrada_pole_int *p_lnpi) int i; for (i = 0; i < p_lnpi->delkapole; i++) printf("%d ", p_lnpi->p_pole[i]); printf("\n"); int tiskdosouboru(luxusni_nahrada_pole_int *p_lnpi) int i; FILE *fw; fw = fopen("data.txt", "w"); if (fw == NULL) printf("soubor nesel otevrit..."); return -1; // neco neslo for (i = 0; i < p_lnpi->delkapole; i++) fprintf(fw, "%d ", p_lnpi->p_pole[i]); fprintf(fw, "\n");
if (fclose(fw) == EOF) printf("soubor nesel zavrit..."); return -1; // neco neslo else return 0; int tiskdocsv(luxusni_nahrada_pole_int *p_lnpi) int i; FILE *fw; fw = fopen("data.csv", "w"); if (fw == NULL) printf("soubor nesel otevrit..."); return -1; // neco neslo for (i = 0; i < p_lnpi->delkapole; i++) fprintf(fw, "%d;", p_lnpi->p_pole[i]); fprintf(fw, "\n"); if (fclose(fw) == EOF) printf("soubor nesel zavrit..."); return -1; // neco neslo else return 0;
int main() LUXUSNI_NAHRADA_POLE_INT *p_sklad_intu; int i; p_sklad_intu = malloc(sizeof(luxusni_nahrada_pole_int)); if (p_sklad_intu == NULL) printf("je to v haji... Neni alokovano...\n"); return(-1); printf("kolik intu bude: "); scanf("%d", &(p_sklad_intu->delkapole)); p_sklad_intu->p_pole = malloc(p_sklad_intu- >delkapole * sizeof(int)); if (p_sklad_intu->p_pole == NULL) printf("je to v haji... Neni alokovano...\n"); return(-1); for (i = 0; i < p_sklad_intu->delkapole; i++) p_sklad_intu->p_pole[i] = 33; tisk(p_sklad_intu); tiskdosouboru(p_sklad_intu); tiskdocsv(p_sklad_intu); free(p_sklad_intu->p_pole); free(p_sklad_intu);
return 0;
Řetězce shrnutí a doplnění - jde o spec. případ polí, speciální pouze v tom, že jde o pole charů - jinak se s nimi jako s běžným polem - mohou deklarována staticky i dynamicky (pomocí malloc) - dohodou je dáno, řetězce se ukončují znakem '\0' viz začátek semestru (konstanty) tzv. nulový byte - pokud tento neuvedu nevadí při použití takového řetězce jako běžného pole. ALE vadí to knihovním funkcím Céčka, které spoléhají, že dle tohoto poznají konec pole. Stejně tak to bude každému programu, který řetězce využívá a na toto ukončení spoléhá. - je to proto, že C nekontroluje meze polí (délky) a já u řetězců nějak musím poznat konec. - tato "norma" se nazývá řetězec ve formátu ASCIZ - znak '\0' nemusí být umístěn až na konci pole, např: char pozdrav[20]; - uložím do něj např. Ahoj uloženo bude: A h o j \0 - tj. mám připraveno pole délky 20, využiji 4 znaky a dám '\0' funkce, které budou např. hledat nějaký znak v řetězci, podřetězec atd., nebudou pracovat s celým polem (tam mne nezajímá co je uloženo ), ale budou pracovat pouze s tím co je tam opravdu uloženo Ahoj
- Pozor: pozor je třeba s jedním znakem navíc (při deklaraci řetězce) protože potřebuji místo na '\0' tj. do našeho řetězce pozdrav mohu uložit max. 19cti znakový text - stejně jako u polí indexujeme od 0. Tj. u řetězce pozdrav je poslední prvek pozdrav[19], ten musí v rezervě na '\0' a poslední možné místo na znak je pozdrav[18]
Řetězcové konstanty - jsou v uvozovkách: "Ahoj jak se máte?" - znak '\0' je zahrnut automaticky (toto pole má 17+1 znak = 18) Deklarace řetězců Tj. např. při inicializaci v deklaraci: char pozdrav[20] = "Ahoj"; - znak '\0' je přidán automaticky, pole má délku 20. char pozdrav[] = "Ahoj"; - znak '\0' je přidán automaticky, pole má délku 5 (Ahoj a '\0'). POZOR: - takovéto přiřazení lze jen při inicializaci C neumí přímo přiřadit řetězec! char pozdrav[20]; pozdrav = "Ahoj"; /* toto v C nelze, ohlásí chybu, pozdrav je pointer na řetězec */ Jak tedy naplnit řetězec? Použít knihovní funkce, includovat soubor <string.h> #include <string.h>...nějaký kód... char pozdrav[20]; char jinypozdrav[30]; strcpy(pozdrav, "Ahoj"); /* naplní (zkopíruje) Ahoj do pozdrav */ strcpy(jinypozdrav, "Nazdar"); /* naplní (zkopíruje) Nazdar do jinypozdrav */
strcpy(pozdrav, jinypozdrav); /* naplní (zkopíruje) jinypozdrav do pozdrav */ Lze narazit na toto (a je to správně): char *p_str = "Ahoj"; - mám pointer na char a přiřadím mu adresu statického řetězce "Ahoj" - tj. je to něco jako int *p_i = &i; Dynamické textové řetězce - stejně jako u 1D dynamických polí Např.: char *p_retez; p_retez = (char *) malloc(20 * sizeof(char)); lze i p_retez = (char *) malloc(20); /* protože sizeof(char) je 1 byte */ - další použití je již stejné jako u statických řetězců. Např.: strcpy(p_retez, "Ahoj"); /* naplní (zkopíruje) Ahoj do p_retez */ p_retez[0] = 'a'; /* v p_retez bude ahoj, jednotlivé znaky lze přiřadit */ Nezapomenout: pokud budu plnit řetězec jinak než konstantní řetězcem (v deklaraci nebo strcpy v programu) nesmím zapomenout připojit '\0' Např.: - mohu např. plnit řetězec znak po znaku, např. naplníme řetězec abeceda znaky A až Z: char abeceda[40]; int i; for (i = 'A'; i <= 'Z'; i++) abeceda[i 'A'] = (char) i;
abeceda['z' 'A' + 1] = '\0'; // za 'Z' dám '\0' nebo jinak: char abeceda[40]; int i; for (i = 0; i <= ('Z'-'A'); i++) abeceda[i] = (char) (i + 'A'); abeceda['z' 'A' + 1] = '\0'; nebo ještě jinak: char abeceda[40]; int znak, j; for (j = 0, znak = 'A'; znak <= 'Z'; znak++, j++) abeceda[j] = (char) znak; abeceda['z' 'A' + 1] = '\0'; Pozor na následující případ: char abeceda[50], *p_ret; int i; for (i = 'A'; i <= 'Z'; i++) abeceda[i - 'A'] = (char) i; abeceda['z' - 'A' + 1] = '\0'; p_ret = abeceda; puts(abeceda); puts(p_ret);
printf("\nabeceda %d %d", sizeof(abeceda), strlen(abeceda)); printf("\np_ret %d %d\n", sizeof(p_ret), strlen(p_ret)); Vytiskne to: ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ abeceda 50 26 p_ret 4 26 - pro proměnnou abeceda, deklarovanou staticky, sizeof vrátí deklarovanou délku pole, ale pro dynamickou deklaraci (pointerem) vrátí velikost pointeru na char (velikost adresy 4byte). Pozor tento jev nastává též při předávání řetězců jako parametrů funkcí!!!!! void moje_funkce(char *p_retez) funkce neco dela, sizeof(p_retez) by byl 4 (velikost pointeru) void main(void) char retez[50] = "Ahoj"; /* v mainu by sizeof(retez) bylo 50 */ moje_funkce(retez); - strlen funguje ve všech případech stejně