CVIČENIE 8/13 (S7) Programovanie v jazyku C - to chce dynamiku About rozdiel medzi statickou a dynamickou alokaciou pamate pre c objekty, spojkove zoznamy Basic knowledge kazda premenna musi mat pocas svojej existencie prideleny pamatovy priestor, ktory svojou velkostou zodpoveda typu premennej. meno premennej (identifikator) je vlastne symblicka adresa tohoto pamatoveho pristoru. nasledujuci priklad vypise, kolko bytov v pamati zabera obsah premennej cislo. int cislo; printf( "velkost premennej cislo v pamati je: %d bytov\n", sizeof(cislo) ); alokacia - akcia, ktora vyhradzuje pamatovy priestor. pozname dva typy: 1. dynamicka alokacia - miesto v pamati (v hromade - heap alebo v zasobniku - stack) sa alokuje za behu programu 2. staticka alokacia - miesto v pamati (v datovej oblasti - data area) sa alokuje uz behom prekladu, teda pred spustenim programu staticka alokacia pouziva sa vtedy, ked dopredu vieme, ake budeme mat pamatove naroky. velkost vyhradenej pamate aj jej miesto je stale rovnake, meni sa len jej obsah (so zmenou premennej pocas behu programu). existencia staticky alokovanych premennych je od zaciatku programu do jeho konca. staticka alokacia pamate je jednoducha a ucinna, ale niekedy nedostatocna. napr. ked potrebujeme nacitat do pamate obsah celeho suboru, nevieme povedat v dobe prekladu, ci to bude dlhy alebo kratky subor. staticka alokacia premennych vymedzuje miesto v datovej oblasti. globalne premenne mozu byt alokovane iba staticky. priklad statickej alokacie:
int cislo; float teplota; dynamicka alokacia pri jej pouziti sa vymedzuje pamat na hromade (heap). principom je, ze za behu programu je mozne pamat dynamicky pridelit (alokovat) oblast pamati urcitej dlzky. tato pamat nie je pomenovana (nema identifikator) a pristupuje sa do nej pomocou pointeru. programovaci jazyk postkytuje nastroje na to, aby pri dynamickom pridelovani pamate nedoslo ku prideleniu uz pridelenej pamate a teda nevznikali kolizie a konflikty. jedna sa o pouzitie funkcie malloc(). ta prideli z heapu pozadovanu pamat a vrati jej adresu. velkost takto pridelenej pamate musime zadat. velkost je ale zavisla od na velkosti objektu, na ktory adresa bude odkazovat. zivotnost takto pridelenej pamate je bud do konca programu alebo pokial nie je tato pamat znova uvolnena volanim funkcie free(). jedinym parametrom funkcie malloc() je parameter typu unsigned int, ktory udava pocet bytov, ktory chceme alokovat. funkcia vracia pointer na void, ktory predstavuje adresu prveho bytu pridelenej pamate. tento pointer je velmi vhodne pretypovat na pointer na odpovedajuci typ. pokial nie je v pamati dost miesta, bude vrateny NULL. ukazka pouzitia spolu s testom pamate: int *p_i; if( (p_i = (int*)malloc(1000) == NULL ) { printf( "Malo pamate more\n" ); exit(1); modifikovany predchadzajuci priklad pre 1000 objektov int: int *p_i; if( (p_i = (int*)malloc(sizeof(int) * 1000) == NULL ) { printf( "Malo pamate more\n" ); exit(1); uvolnenie takto alokovanej pamate sa vykona nasledovne: free(p_i);
TODO : void* malloc (int size) - parametrem je počet bytů, co chceme alokovat - funkce vrací adresu na první přidělený prvek - není-li místo, funkce vrací NULL funkce vrací ukazatel na void: void* malloc(...)!! Př. chci alokovat např. paměť pro strukturu tstr Krok 1: - definujeme strukturu tstr typedef struct {... tstr; Krok 2: - definujeme pointer tp na danou strukturu - pointer bude ukazovat někam do paměti tstr* tp; tp Krok 3: alokujeme místo pro strukturu tstr a pointer tp nasměrujeme na alokovanou strukturu tp v C: tp = malloc(sizeof(tstr)) OK... do tp dám void pointer C umí implicitně přetypovat typovaný pointer (pointer na nějaký daná typ) na voidový ukazatel v C++: tp = malloc(sizeof(tstr)) NEJDE nutno explicitně přetypovat na typovaný pointer tp = (tstr*)malloc(sizeof(tstr)) C++ není nadmnožina C sizeof (z) free (pointer na začátek bloku) - uvolní paměť
- když žádáme o paměť, je věcí OS, kolik paměti z heapu skutečně přidělí důvodem je, že system musí mít pro každý blok nějakou administrativu - nelze zapisovat ani o kousek míň ani víc (na nižší ani vyšší adresu) - časté chyby: přepisování začátků a konců bloků, kde je administrativa alokovaná paměť uživatele administrativa - jak poznat kropiče? - udělat si vlastní malloc - naalokovat si o trošku víc - na začátek a konec dát vlastní breakpointy Ukazatelé a struktury typedef struct { int a; t; t x; přístup k položkám struktury x.a t* px; přístup k položkám struktury (*px).a px->a v (*px).a musí být závorky, protže tečka má vyšší prioritu!! Př. zásobník 1. jaké struktury? 2. jaké knihovní rozhraní? o init (inicializace zásobníku) o push o pop o find (zda se prvek x vyskytuje v zasobniku odpověď 0 nebo 1) 3. jaké použití?!! pozor na ubírání a vkládání z/do prázdného zásobníku!! Řešení #include <stdio.h> #include <stdlib.h>
typedef struct prvek_ { int x; struct prvek_* dalsi; prvek; typedef struct { prvek* vrchol; zas; z Přidání prvku // vlozeni prvku do zasobniku int push (zas* z, int x) { prvek* n; n = (prvek*) malloc (sizeof(prvek)); if (!n) n->x = x; n->dalsi = z->vrchol; z->vrchol = n; Přidání prvku do prázdného zásobníku // vybrani prvku ze zasobniku int pop (zas* z) { int x; prvek* p = z->vrchol; if (!p) return -1; x = p->x; z->vrchol = p->dalsi; free(p); return x; z // inicializace zasobniku zas* init(zas* z) { z = (zas*) malloc (sizeof(zas)); if (!z) return NULL; z->vrchol = NULL; return z;
// zjisteni, zda prvek x je ci neni v zasobniku int find(zas* z, int x) { prvek* u; if (!z) return 2; u = z->vrchol; while (u && u->x!=x) u = u->dalsi; if (!u) else // vypsani zasobniku int print(zas* z) { prvek* u; if (!z) { printf("zas neni inicializovany \n"); u = z->vrchol; printf("zas: \n"); while (u) { printf("prvek: %d \n",u->x); u = u->dalsi; printf("\n"); // main int main (int args, char** argv) { zas* za = NULL; za = init(za); push(za,1); push(za,3);
push(za,2); pop(za); pop(za); Typické chyby int push (prvek* zac, int x) - předávání parametrů hodnotou!! - potřebujeme ve funkci posunout ukazatel na nový začátek zásobníku provizorní řešení: funkce bude vracet pointer na nový začátek!! toto ale nepůjde u fronty, která je charakterizována 2 pointery Př. naprogramovat frontu #include <stdio.h> #include <stdlib.h> typedef struct prvek { int x; struct prvek* dalsi; prvek; typedef struct { prvek* prvni; prvek* posledni; fronta;
// inicializace fronty fronta* init(fronta* f){ f = (fronta*) malloc (sizeof(fronta)); if (!f) return NULL; f->prvni = NULL; f->posledni = NULL; return f; // vlozeni prvku na konec fronty int push(fronta* f, int x){ prvek* p = (prvek*) malloc(sizeof(prvek)); if (!p) return 2; p->x = x; p->dalsi = NULL; posledni prvni if (!f->posledni) { f->prvni = p; f->posledni = p; else { f->posledni->dalsi = p; f->posledni = p; // vybrani prvku ze zacatku fronty int pop(fronta* f){ int x; prvek* p; p = f->prvni; posledni prvni // ve frontě není prvek if (!p) { printf("fronta je uz prazdna \n"); return -1; if (f->posledni == p) f->posledni = NULL; // ve frontě byl jediný prvek a ten odstraňujeme f->prvni = p->dalsi; x = p->x; free(p);
return x; int print(fronta* f) { prvek* u; if (!f) { printf("fronta neni inicializovana \n"); u = f->prvni; printf("fronta: \n"); while (u) { printf("prvek: %d \n",u->x); u = u->dalsi; printf("\n"); int main (int args, char** argv) { fronta* fr = NULL; fr = init(fr); push(fr,1); push(fr,3); pop(fr); push(fr,2); pop(fr); pop(fr); pop(fr);