Ukazatele #2, dynamická alokace paměti

Podobné dokumenty
IUJCE 07/08 Přednáška č. 6

Struktura programu v době běhu

Pointery II. Jan Hnilica Počítačové modelování 17

Ukazatele, dynamická alokace

BI-PA1 Programování a Algoritmizace 1. Miroslav Baĺık, Ladislav Vagner a Josef Vogel. 7., 9. a 10. listopadu 2017

BI-PA1 Programování a algoritmizace 1, ZS Katedra teoretické informatiky

Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace

Správné vytvoření a otevření textového souboru pro čtení a zápis představuje

Správa paměti. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta, 2016

IUJCE 07/08 Přednáška č. 4. v paměti neexistuje. v paměti existuje

Ukazatele #1, struktury

Více o konstruktorech a destruktorech

Mělká a hluboká kopie

BI-PA1 Programování a algoritmizace 1 Katedra teoretické informatiky

Př. další použití pointerů

6. lekce Úvod do jazyka C knihovny datové typy, definice proměnných základní struktura programu a jeho editace Miroslav Jílek

for (int i = 0; i < sizeof(hodnoty) / sizeof(int); i++) { cout<<hodonoty[i]<< endl; } cin.get(); return 0; }

Správa paměti. doc. Ing. Miroslav Beneš, Ph.D. katedra informatiky FEI VŠB-TUO A-1007 /

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Práce s pamětí a předávání parametrů. Úvod do programování 1

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

Dynamická vícerozměrná pole. Základy programování 2 Tomáš Kühr

Základy programování (IZP)

Práce s polem a pamětí

Základy programování (IZP)

Střední škola pedagogická, hotelnictví a služeb, Litoměříce, příspěvková organizace

Funkce, procedury, složitost

ZPRO v "C" Ing. Vít Hanousek. verze 0.3

09. Memory management. ZOS 2006, L.Pešička

Programování v jazyce C a C++

Základy programování 2 KMI/ZP2

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

8. lekce Úvod do jazyka C 3. část Základní příkazy jazyka C Miroslav Jílek

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

Distanční opora předmětu: Programování v jazyce C Tématický blok č. 6: Dynamická alokace paměti, typové konstrukce Autor: RNDr. Jan Lánský, Ph.D.

IAJCE Přednáška č. 8. double tprumer = (t1 + t2 + t3 + t4 + t5 + t6 + t7) / 7; Console.Write("\nPrumerna teplota je {0}", tprumer);

Abstraktní třídy, polymorfní struktury

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) 28. prosince Fakulta elektrotechniky a informatiky Katedra softwarových technologií

Základy programování (IZP)

Ukazatel (Pointer) jako datový typ - proměnné jsou umístěny v paměti na určitém místě (adrese) a zabírají určitý prostor (počet bytů), který je daný

Práce s binárními soubory. Základy programování 2 Tomáš Kühr

Algoritmizace a programování

Operační systémy. Cvičení 4: Programování v C pod Unixem

Dědění, polymorfismus

Přednáška. Správa paměti I. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) Fakulta elektrotechniky a informatiky Katedra softwarových technologií

Ukazatele, paměťové třídy, volání funkcí

Základy programování (IZP)

8 Třídy, objekty, metody, předávání argumentů metod

Sdílení dat mezi podprogramy

Dynamická alokace paměti

Vícerozměrná pole. Úvod do programování 2 Tomáš Kühr

Úvod do programovacích jazyků (Java)

PB071 Programování v jazyce C Jaro 2017

Pokročilé programování v jazyce C pro chemiky (C3220) Pokročilá témata jazyka C++

PB161 Programování v jazyce C++ Přednáška 5

PB161 Programování v jazyce C++ Přednáška 4

11a Dynamické dvourozměrné pole (obdobně vícerozměrné)

9. lekce Úvod do jazyka C 4. část Funkce, rekurze Editace, kompilace, spuštění Miroslav Jílek

Čtvrtek 8. prosince. Pascal - opakování základů. Struktura programu:

int ii char [16] double dd název adresa / proměnná N = nevyužito xxx xxx xxx N xxx xxx N xxx N

NMIN102 Programování /2 Z, Zk

Jazyk C++, některá rozšíření oproti C

Funkce, intuitivní chápání složitosti

Lineární spojový seznam (úvod do dynamických datových struktur)

Programování v C++, 2. cvičení

Pole a Funkce. Úvod do programování 1 Tomáš Kühr

Základní způsoby: -Statické (přidělění paměti v čase překladu) -Dynamické (přiděleno v run time) v zásobníku na haldě

MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ

Strukturu lze funkci předat: (pole[i])+j. switch(výraz) velikost ukazatele

Základní způsoby: -Statické (přidělění paměti v čase překladu) -Dynamické (přiděleno v run time) v zásobníku na haldě

Pokročilé programování v jazyce C pro chemiky (C3220) Třídy v C++

4. Typ ukazatel, strukturované datové typy

Operační systémy. Přednáška 7: Správa paměti I

Preprocesor. Karel Richta a kol. katedra počítačů FEL ČVUT v Praze. Karel Richta, Martin Hořeňovský, Aleš Hrabalík, 2016

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Aplikace Embedded systémů v Mechatronice. Michal Bastl A2/713a

PB071 Programování v jazyce C Jaro 2015

Rekurzivní algoritmy

Konstruktory a destruktory

Abstraktní datové typy, moduly

Metody připojování periferií BI-MPP Přednáška 2

2 Datové typy v jazyce C

PB přednáška (12. října 2015)

Koncepce (větších) programů. Základy programování 2 Tomáš Kühr

PB161 Programování v jazyce C++ Přednáška 4

konstruktory a destruktory (o)

7. Datové typy v Javě

Programování v jazyce C a C++

ZÁKLADY PROGRAMOVÁNÍ V C

Operační systémy. Správa paměti (SP) Požadavky na SP. Spojování a zavedení programu. Spojování programu (linking) Zavádění programu (loading)

Úvod do programování - Java. Cvičení č.4

C++ přetěžování funkcí a operátorů. Jan Hnilica Počítačové modelování 19

Pole stručný úvod do začátku, podrobně později - zatím statická pole (ne dynamicky) - číslují se od 0

Základy C++ I. Jan Hnilica Počítačové modelování 18

Algoritmizace a programování

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Programování v C++ Úplnej úvod. Peta (maj@arcig.cz, SPR AG )

Transkript:

Ukazatele #2, dynamická alokace paměti BI-PA1 Programování a Algoritmizace 1 Miroslav Baĺık, Ladislav Vagner a Josef Vogel Katedra teoretické informatiky a Katedra softwarového inženýrství Fakulta informačních technologíı České vysoké učení technické v Praze xvagner@fit.cvut.cz, vogeljos@fit.cvut.cz 21. a 30. listopadu 2017 a 1. prosince 2017

Přehled Ukazatelová aritmetika. Ukazatele a pole. Ukazatele a řetězce. Dynamická alokace paměti. Dynamická alokace vícerozměrných poĺı. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 2/43

Aritmetika ukazatelů motivace Chceme-li v C implementovat výstupní parametr, deklarujeme funkci např. takto: void foo ( int * outparam,... ) Chceme-li v C předat funkci pole, máme dvě ekvivalentní možnosti deklarace: void foo ( int * array,... ) void foo ( int array [],... ) Výše uvedené zápisy jsou shodné, tedy v C se pole chová (téměř) stejně jako ukazatel. Protože můžeme pomocí indexů přistupovat k jednotlivým prvků pole, musí tuto možnost v nějaké formě nabízet i ukazatel máme k dispozici ukazatelovou aritmetiku. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 3/43

Aritmetika ukazatelů K ukazateli je možno přičíst (nebo odečíst) celočíselnou hodnotu, ukazatele je možno dále odečítat a porovnávat. Následuje přehled povolených operací: T* + int -> T* T* - int -> T* T* - T* -> int T* rel op T* -> int Když je celé číslo n přičteno k ukazateli T*, výsledná adresa je zvětšena o n * sizeof ( T ) bajtů. Podobné je to pro odečtení celočíselné hodnoty. Jestliže jsou dva ukazatele odečteny, je výsledkem celé číslo, jehož hodnotou je počet prvků typu (T) mezi ukazateli. Výsledek porovnání ukazatelů je dán hodnotami adres (větší, stejné, menší). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 4/43

Ukazatele aritmetika int a[10], *p = &a[0]; *(p + 3) = 10; /* hodnota a[3] je ted 10 */ /* jak vynulovat pole */ for ( p = &a[0]; p <= &a[9]; p++ ) *p = 0; /* nebo */ for ( p = &a[0]; p <= &a[9]; *p++ = 0 ); /* nebo ještě lépe */ for ( p = &a[0]; p <= &a[9]; *p++ = 0 ) {} M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 5/43

Ukazatele a pole C má speciální zpracování staticky alokovaných poĺı. Staticky alokované C pole může pouze: vyhodnotit svou velikost použitím operátoru sizeof, implicitně se zkonvertovat na konstantní ukazatel ukazující na nultý prvek pole. int a[10], *pa; pa = a; /* ekvivalentní pa = &a[0] */ *(pa + 2) = 10; /* a[2] = 10; */ *(a + 3) = 5; /* a[3] = 5; */ pa[4] = 100; /* a[4] = 100 */ for (pa = a; pa <= a + 9; *pa++ = 0) {} a++; /* Chyba. Proč? */ když a je pole (nebo ukazatel) a i je celé číslo, tak platí: a[i] = *(a+i) = *(i+a) = i[a] M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 6/43

Ukazatele a pole Problém přečíst vektor, který má n komponent. /* verze s "polem" */ void readvector1 ( int v[], int n ) { int i; printf ( "Napis %d cisel:\n", n ); for ( i = 0; i < n; i ++ ) scanf ( "%d", &v[i] ); } /* verze s "ukazatelem" (vylepšená) */ void readvector2 ( int * p, int n ) { int * q = p + n; printf ( "Napis %d cisel:\n", n ); for ( ; p < q; p++ ) scanf ( "%d", p ); } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 7/43

Ukazatele a řetězce Palindrom je text (dlouhý nejméně dva znaky), který se čte stejně zepředu jako zezadu. Několik příkladů: kayak, murder for a jar of red rum, don t nod.... Vytvoříme funkci, která testuje palindromy. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 8/43

Ukazatele a řetězce #define MAX_LEN 100 int ispalindrome ( char *s ); int main ( void ) { char phrase[max_len+1]; printf ( "Napis frazi, max %d znaku:\n", MAX_LEN ); scanf ( "%100s", phrase ); printf ( "Fraze %s ", phrase ); if ( ispalindrome(phrase) ) printf ( "je" ); else printf ( "neni" ); printf ( " palindrom\n"); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 9/43

Ukazatele a řetězce Jak otestujeme palindrom? Fráze je uložena v poli znaků od indexu 0, poslední použitý index je l-1, kde l je délka fráze, znak s indexem l je zakončovací nula. Potřebujeme porovnávat znaky: s[0] a s[l-1] s[1] a s[l-2]... porovnávání končí ve středu fráze. int ispalindrome ( char *s ) { int l = strlen ( s ), half = l/2, i; for ( i = 0; i < half; i++) if (s[i]!= s[l-i-1]) return 0; return 1; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 10/43

Ukazatele a řetězce Jiné řešení: Použijeme dva ukazatele, první ukazatel bude na počátku referencovat první znak a bude postupovat směrem ke konci fráze, druhý ukazatel bude na počátku referencovat poslední znak a bude postupovat směrem k začátku fráze, pokud se znaky referencované ukazateli shodují, je to palindrom int ispalindrome ( char *s ) { char * e; for ( e = s + strlen ( s ) - 1; s < e; s ++, e-- ) if ( *s!= *e ) return 0; return 1; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 11/43

Dynamická alokace paměti Hlavní použití ukazatelů v jazyce C: předání výstupních (referencovaných) parametrů funkci, předání poĺı jako parametrů funkci, manipulace s poli (zejména s řetězci), přístup k dynamicky alokované paměti. Dynamicky alokovaný objekt (proměnná) nevzniká deklarací. Místo toho vzniká dynamicky, v době běhu programu, pomocí speciálních funkcí (operátorů). Dynamicky alokovaná proměnná nemá jméno. Pro přístup k proměnné slouží její adresa (ukazatel). V C dynamickou alokaci poskytuje funkce malloc: funkce má jeden parametr velikost alokované paměti v bajtech, funkce rezervuje souvislý blok požadované velikosti v paměti počítače, funkce vrací ukazatel (adresu) takto připraveného bloku, návratový ukazatel je typu void*, obvykle je třeba jej přetypovat (typecast). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 12/43

Dynamická alokace paměti Příklad dynamické alokace: int main( void ) { int *p; } p = (int*)malloc( sizeof(int) ); *p = 10; printf ( "*p=%d\n", *p ); return 0; Proměnná p je ukazatel. Tento ukazatel je normální lokální proměnná (tj. alokovaná kompilátorem na zásobníku). Ukazatel referencuje blok dynamicky alokované paměti: blok je anonymní v programu nemá jméno, přístup k němu je možný jen prostřednictvím ukazatele p. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 13/43

Dynamická alokace paměti Dynamická alokace primitivních datových typů je třeba jen zřídka: primitivní datové typy jsou malé, režie přidělování bloku paměti je mnohem větší než vlastní blok. Dynamická alokace paměti je dobře využitelná pro alokaci větších (strukturovaných) proměnných poĺı nebo struktur. Příklad: funkce čte obsah vektoru dané dimenze n. int * readvector( int n ) { int i, *p; p = (int*)malloc ( sizeof(int)*n ); printf("napis %d cisel:\n", n); for ( i = 0; i < n; i ++ ) scanf( "%d", &p[i] ); return p; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 14/43

Dynamická alokace paměti Co se stane, když malloc špatně předáme velikost? int i, n = 5; double * a = (double *) malloc ( n ); /*!!! */ for ( i = 0; i < n; i ++ ) a[i] = 0; i n... 5 a reserved size: 5B... a[0] a[1]... Předpokládáme sizeof (int) = 4 a sizeof (double) = 8. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 15/43

Dynamická alokace paměti Co se stane, když malloc špatně předáme velikost? int i, n = 5; double * a; a = (double *) malloc ( n * sizeof ( int ) ); /*!!! */ for ( i = 0; i < n; i ++ ) a[i] = 0; i n... 5 a reserved size: 20B a[0] a[1] a[2] a[3] a[4] M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 16/43

Dynamická alokace paměti Co se stane, když malloc špatně předáme velikost? int i, n = 5; double * a; a = (double *) malloc ( n * sizeof ( double ) ); for ( i = 0; i < n; i ++ ) a[i] = 0; i n... 5 a reserved size: 40B a[0] a[1] a[2] a[3] a[4] M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 17/43

Dynamická alokace paměti Starost o velikost prvku můžete nechat na kompilátoru. Místo: int n =...; int * array; array = (int*) malloc ( n * sizeof ( int ) ); Je lepší zapsat: array = (int*) malloc ( n * sizeof ( *array ) ); Je to takový rozdíl? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 18/43

Dynamická alokace paměti Co je správně? int ** a, n =...; a = (int**) malloc ( n * sizeof (int) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 19/43

Dynamická alokace paměti Co je správně? int ** a, n =...; a = (int**) malloc ( n * sizeof (int) ); /* ERROR */ a = (int**) malloc ( n * sizeof (int *) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 19/43

Dynamická alokace paměti Co je správně? int ** a, n =...; a = (int**) malloc ( n * sizeof (int) ); /* ERROR */ a = (int**) malloc ( n * sizeof (int *) ); /* OK */ a = (int**) malloc ( n * sizeof ( *a ) ); /* OK */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 19/43

Dynamická alokace paměti int (* a)[5], n =...; a = (int(*)[5]) malloc ( n * sizeof (int) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 20/43

Dynamická alokace paměti int (* a)[5], n =...; a = (int(*)[5]) malloc ( n * sizeof (int) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int *) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 20/43

Dynamická alokace paměti int (* a)[5], n =...; a = (int(*)[5]) malloc ( n * sizeof (int) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int *) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int (*)[5]) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 20/43

Dynamická alokace paměti int (* a)[5], n =...; a = (int(*)[5]) malloc ( n * sizeof (int) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int *) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int (*)[5]) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int [5]) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 20/43

Dynamická alokace paměti int (* a)[5], n =...; a = (int(*)[5]) malloc ( n * sizeof (int) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int *) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int (*)[5]) ); /* ERROR */ a = (int(*)[5]) malloc ( n * sizeof (int [5]) ); /* OK */ a = (int(*)[5]) malloc ( n * sizeof ( *a ) ); /* OK */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 20/43

Dynamická alokace paměti int * (**a)(int *), n =...; a = (int * (**)(int*)) malloc ( n * sizeof(int) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 21/43

Dynamická alokace paměti int * (**a)(int *), n =...; a = (int * (**)(int*)) malloc ( n * sizeof(int) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof(int *) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 21/43

Dynamická alokace paměti int * (**a)(int *), n =...; a = (int * (**)(int*)) malloc ( n * sizeof(int) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof(int *) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof(int **) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 21/43

Dynamická alokace paměti int * (**a)(int *), n =...; a = (int * (**)(int*)) malloc ( n * sizeof(int) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof(int *) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof(int **) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof (int * (*)(int*) ) ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 21/43

Dynamická alokace paměti int * (**a)(int *), n =...; a = (int * (**)(int*)) malloc ( n * sizeof(int) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof(int *) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof(int **) ); /* ERROR */ a = (int * (**)(int*)) malloc ( n * sizeof (int * (*)(int*) ) ); /* OK */ a = (int * (**)(int*)) malloc ( n * sizeof ( *a ) ); /* OK */ Proč si přidělávat starosti s určením správného zápisu datového typu, když to kompilátor dokáže automaticky a bez chyb? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 21/43

Dynamická alokace paměti Čím je alokovaný blok vyplněn (inicializován)? obsah není definovaný (neinicializováno), při prvních alokacích bývá obsah vyplněn nulovými bajty, ale NELZE se na to spolehnout. Proč malloc obsah vždy nevynuluje? vyplnění nulami stojí nenulový čas, vyplnění nulami není potřeba vždy. Pokud ihned po alokaci prostor vyplníme novým užitečným obsahem, bylo nulování zbytečné, pokud by se nulovalo vždy, bylo by zpracování např. obrazu, videa a zvuku zbytečně neefektivní a zbytečně by vyžadovalo výkonnější HW, pokud záměrně chcete mít prostor vždy vynulovaný, použijte calloc místo malloc. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 22/43

Proč je potřeba přetypování a co vlastně dělá Funkce malloc je obyčejná funkce ve std. knihovně. Její rozhraní je: void * malloc ( size_t size ); Návratový typ je void*, tedy ukazatel (adresa) bez udaného typu. Pokud použijeme funkci malloc přímo, nebude v přiřazení souhlasit datový typ ukazatelů: int * array = malloc ( 1000 * sizeof ( int ) ); /* levá strana int * vs. pravá strana void * */ Kompilátor takové přiřazení odmítne přeložit, případně u něj generuje varování. Není si jist, zda se nejedná o programátorskou chybu (z minulé přednášky víme, že přiřazení mezi ukazateli různých typů může vést k těžko odhalitelným chybám). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 23/43

Proč je potřeba přetypování a co vlastně dělá Přetypování zde říká kompilátoru, že jsme si vědomi nesouladu typů a že tento nesoulad je zamýšlený (protože je daný deklarací funkce malloc). Odpojíme tím tedy dobře míněné kontroly kompilátoru a přebíráme zodpovědnost za veškerá rizika! Přetypování ukazatelů je za běhu prázdná (no-op) operace. Adresa z návratové hodnoty je beze změn uložena do proměnné na levé straně. Vždy si bud te jisti, že funkci malloc používáte správně. Zvláštní péči věnujte velikosti alokovaného prostoru. Pokud alokujete n prvků, nezapomeňte hodnotu n vynásobit velikostí prvku (operátor sizeof). Uvědomte si, že kompilátor Vám zde případné chyby nenahlásí, protože přetypováním jsme jej úmyslně odpojili. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 24/43

Dynamická alokace paměti Dynamicky alokovaná pamět je rezervována až do okamžiku, kdy ji programátor dealokuje. Co když dynamicky alokovaná pamět není dealokována? int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; p = q; p? q? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 25/43

Dynamická alokace paměti Dynamicky alokovaná pamět je rezervována až do okamžiku, kdy ji programátor dealokuje. Co když dynamicky alokovaná pamět není dealokována? int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; p = q; p q?? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 25/43

Dynamická alokace paměti Dynamicky alokovaná pamět je rezervována až do okamžiku, kdy ji programátor dealokuje. Co když dynamicky alokovaná pamět není dealokována? int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; p = q; p q?? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 25/43

Dynamická alokace paměti Dynamicky alokovaná pamět je rezervována až do okamžiku, kdy ji programátor dealokuje. Co když dynamicky alokovaná pamět není dealokována? int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; p = q; p q? 10? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 25/43

Dynamická alokace paměti Dynamicky alokovaná pamět je rezervována až do okamžiku, kdy ji programátor dealokuje. Co když dynamicky alokovaná pamět není dealokována? int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; p = q; p q 20? 10? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 25/43

Dynamická alokace paměti Dynamicky alokovaná pamět je rezervována až do okamžiku, kdy ji programátor dealokuje. Co když dynamicky alokovaná pamět není dealokována? int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; p = q; 20? 10? p q inaccessible, cannot free M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 25/43

Dynamická alokace paměti Dynamicky alokovaná pamět je rezervována až do okamžiku, kdy ji programátor dealokuje. Co když dynamicky alokovaná pamět není dealokována? int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; p = q; 20? 10? p q inaccessible, cannot free Ztracené bloky stále patří programu. Když se program nestará o ztracené bloky, může vyčerpat celou pamět. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 25/43

Dynamická alokace paměti Bloky paměti je potřeba před ztrátou reference uvolnit. V C není automatická správa volné paměti (garbage collector). int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( q ); p q 20 10 M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 26/43

Dynamická alokace paměti Bloky paměti je potřeba před ztrátou reference uvolnit. V C není automatická správa volné paměti (garbage collector). int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( q ); p q 20 marked as unused... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 26/43

Dynamická alokace paměti Bloky paměti je potřeba před ztrátou reference uvolnit. V C není automatická správa volné paměti (garbage collector). int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( q ); p q 20 marked as unused... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 26/43

Dynamická alokace paměti Bloky paměti je potřeba před ztrátou reference uvolnit. V C není automatická správa volné paměti (garbage collector). int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( q ); p q marked as unused... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 26/43

Dynamická alokace paměti Každý alokovaný blok by měl být uvolněn. Blok je potřeba uvolnit právě jednou. Pokus o vícenásobné uvolnění bloku může vést k pádu programu. int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( p ); /* note: p */ free ( q ); p q 20 10 M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 27/43

Dynamická alokace paměti Každý alokovaný blok by měl být uvolněn. Blok je potřeba uvolnit právě jednou. Pokus o vícenásobné uvolnění bloku může vést k pádu programu. int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( p ); /* note: p */ free ( q ); p q 20 marked as unused... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 27/43

Dynamická alokace paměti Každý alokovaný blok by měl být uvolněn. Blok je potřeba uvolnit právě jednou. Pokus o vícenásobné uvolnění bloku může vést k pádu programu. int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( p ); /* note: p */ free ( q ); p q 20 marked as unused... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 27/43

Dynamická alokace paměti Každý alokovaný blok by měl být uvolněn. Blok je potřeba uvolnit právě jednou. Pokus o vícenásobné uvolnění bloku může vést k pádu programu. int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( p ); /* note: p */ free ( q ); p q marked as unused... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 27/43

Dynamická alokace paměti Každý alokovaný blok by měl být uvolněn. Blok je potřeba uvolnit právě jednou. Pokus o vícenásobné uvolnění bloku může vést k pádu programu. int *p, *q; p = (int*) malloc ( ); q = (int*) malloc ( ); *p = 10; *q = 20; free ( p ); p = q; free ( p ); /* note: p */ free ( q ); p q CRASH, double free M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 27/43

Dynamická alokace paměti Kdy musíme použít dynamickou alokaci paměti: v době kompilace neznáme velikost pole, známe velikost pole, ale pole je veliké (např. stovky KiB), tedy nemusí se vejít na zásobník jako lokální proměnná, pro alokaci velkých struktur, potřebujeme pole (či strukturu) vytvořit ve funkci a předat ji volajícímu. V ostatních případech může být lepší použít alokace statické (lokální proměnné). Statická alokace je vhodná pro: alokaci jednotlivých proměnných primitivních typů (int, double,... ). Tyto proměnné jsou velmi malé (několik bajtů), režie dynamická alokace bude násobně větší, malá pole s omezením velikosti známým v době kompilace, které není potřeba předávat volajícímu. Taková pole lze alokovat staticky a oželet část případně nevyužitého prostoru, malé struktury, které není potřeba předávat volajícímu. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 28/43

Vícerozměrná pole Vícerozměrná pole lze v C alokovat plně staticky, plně dynamicky nebo kombinací statické a dynamické alokace. Každý z přístupů má své výhody a nevýhody. Postupy si ukážeme pro 2D pole (matice), nechají se použít i pro vícerozměrná pole (3D, 4D,... ). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 29/43

Vícerozměrná pole plně statická Známe-li velikost matice v době kompilace, lze ji alokovat staticky: #define ROWS 3 #define COLS 2 int mat[rows][cols]; Kompilátor takovou matici ukládá do paměti po řádcích: řádky jsou uložené těsně za sebou, prvky v řádce jsou také uložené těsně za sebou. mat[2][1] mat[0] mat[1] mat[2] M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 30/43

Vícerozměrná pole plně statická Chceme-li takovou matici předat do funkce jako parametr, musíme předat ukazatel na její počátek: matice je vlastně pole o 3 prvcích, prvkem tohoto pole jsou dvouprvková pole, předáváme tedy ukazatel na dvouprvková pole hodnot typu int. void foo ( int (*mat)[cols], int rows )... Alternativou je zápis pomocí hranatých závorek: void foo ( int mat[][cols], int rows )... Vždy se předává ukazatel na počátek matice v paměti. Protože ukazatel nenese informaci o délce (zde o počtu řádek), je potřeba přidat další parametr. Matici nelze předat pomocí typu int* ani int **. Typ int* umožní předávat 1D pole, typ int** umožní předat dynamicky alokované 2D pole. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 31/43

Vícerozměrná pole dynamicky alokovaná Neznáme-li rozměry matice v době kompilace, musíme matici alokovat dynamicky. Jednotlivé řádky alokujeme jako 1D pole, odkazy na tyto řádky umístíme do pomocného pole: cols rows M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 32/43

Vícerozměrná pole dynamicky alokovaná Alokace matice: int ** mat = (int **) malloc (rows * sizeof (*mat)); for ( i = 0; i < rows; i ++ ) mat[i] = (int *) malloc (cols * sizeof (**mat)); Uvolnění matice: for ( i = 0; i < rows; i ++ ) free ( mat[i] ); free ( mat ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 33/43

Vícerozměrná pole dynamicky alokovaná Chceme-li dynamicky alokovanou matici předat funkci, musíme předat ukazatel na počátek pomocného pole odkazů. Protože předávaný ukazatel nemá informaci o délce pole odkazů, musíme předat i počet řádek. Dále, protože ukazatele v pomocném poli odkazů nemají informace o délce řádky, musíme předat další parametr počet sloupců. void foo ( int ** mat, int rows, int cols )... Pro takovou matici může být vhodné vytvoření struktury se třemi složkami: ukazatel na pomocné pole odkazů, počet řádek, počet sloupců. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 34/43

Vícerozměrná pole kombinace Je-li počet řádek fixní a počet sloupců proměnný, je řešení podobné dynamicky alokované matici. Pomocné pole odkazů však lze alokovat staticky: #define ROWS... int * mat[rows]; /* pole ukazatelů */ for ( i = 0; i < ROWS; i ++ ) mat[i] = (int *) malloc (cols * sizeof (**mat)); Uvolnění matice spočívá v uvolnění jednotlivých řádek: for ( i = 0; i < ROWS; i ++ ) free ( mat[i] ); /* free ( mat ); - zde NE */ Funkci se matice předává stejně jako kdyby byla dynamicky alokovaná (int ** ukazatel). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 35/43

Vícerozměrná pole kombinace Je-li počet sloupců fixní a počet řádek proměnný, je řešení podobné staticky alokované matici. Pracujeme s ukazatelem, který ukazuje na řádek, kde řádek je pole s fixním počtem prvků. Zápis takového ukazatele je méně obvyklý: #define COLS... int (*mat)[cols]; /* ukazatel na prvek typu pole */ /* závorky jsou zde nutné */ mat = (int(*)[cols]) malloc (rows * sizeof (*mat)); Uvolnění matice je triviální: free ( mat ); Funkci se matice předává stejně jako kdyby byla staticky alokovaná (int (*)[COLS] ukazatel). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 36/43

Pamět programu Správa paměti dnešního počítače je velmi komplikovaná. OS musí řešit následující požadavky: virtuální pamět, stránkování, segmentace, pamět ově mapované I/O, sdílení paměti mezi procesy, dynamicky linkované knihovny, vlákna a jejich oddělené zásobníky, DMA, TLS,... Více o této problematice bude řečeno v předmětu BI-OSY. Následující model správy paměti jednoho procesu je velmi zjednodušený, uvažuje pouze jediné vlákno (typický program v PA1) a nezabývá se dynamicky linkovanými knihovnami. Přesnější model získáte například takto (OS Linux): > cat /proc/<pid>/maps M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 37/43

Pamět programu Typický OS (Linux, Windows, Solaris,... ) vytváří běžícímu programu iluzi, že běží na počítači sám: dostane k dispozici plný adresní prostor (4 GiB pro 32 bitový systém, 16 EiB pro 64 bitový systém), v tomto adresním prostoru není jiný program, do tohoto prostoru si program umístí data, která potřebuje pro svůj běh. Program v C typicky potřebuje následující úseky paměti: pamět pro kód (instrukce programu), pamět pro konstanty, pamět pro globální proměnné, pamět pro zásobník (volání funkcí, lokální proměnné), pamět pro haldu (dynamicky alokované proměnné). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 38/43

Pamět programu 4 GiB / 16 EiB stack limit stack, rw- unused uninitialized global variables (zero filled), rw-.bss heap, rw- initialized global variables, rw- constants, r--.data data segment program code, r-x.text executable file constants code standard libraries (libc, libm,...), r-x 0x00000000 OS overhead M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 39/43

Pamět programu Při spuštění připraví OS segmenty pro: kód, konstanty a globální proměnné (fixní velikost), halda je po spuštěni prázdná nebo velmi malá, halda se rozšiřuje podle požadavků na alokace. Zásobník je vyhrazen v horní části paměti: malá počáteční velikost (kilobajty), další pamět je přidána podle potřeby, velmi omezená max. hloubka (jednotky MiB), překročení limitu vede k pádu programu (např. při nekonečné rekurzi). Většina adresního prostoru je neobsazená: typický program v PA1 má kód velikosti řádově desítek KiB, při spuštění se k němu přidávají standardní knihovny (cca 10 MiB) a mělký zásobník, zbytek adresního prostoru (4 GiB - 10 MiB, resp. 16 EiB - 10 MiB) je neobsazen, pokus o přístup do neobsazené části pamět ového prostoru vede k pádu programu. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 40/43

Pamět programu Důsledky tohoto rozdělení paměti: Globální proměnné jsou alokované stále. Použitelné pouze pro data, která program potřebuje uchovávat po celou dobu běhu. Globální proměnné mají fixní velikost danou při kompilaci. Tedy nejsou vhodné pro data, která mají velké rozpětí velikosti (program vždy požaduje mnoho paměti při startu, i když zpracovává málo dat). Globální proměnné se hodí pro konstanty a předpočítané tabulky, které se během výpočtu nemění. Na zásobník nepatří velké proměnné. Zásobník je mělký. Tedy lokální proměnné se nehodí např. pro velká pole. Lokální proměnné se nedají použít, pokud na ně chceme vytvořit odkaz a ten předat volajícímu. Velké proměnné patří na haldu (dynamická alokace). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 41/43

Pamět programu Kde se alokují proměnné? int a = 10; int b; static int c; int main ( void ) { int x; int y = 10; static int z; static int w = 10; int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; static int c; int main ( void ) { int x; int y = 10; static int z; static int w = 10; int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; int main ( void ) { int x; int y = 10; static int z; static int w = 10; int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; int y = 10; static int z; static int w = 10; int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; /* stack */ int y = 10; static int z; static int w = 10; int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; /* stack */ int y = 10; /* stack */ static int z; static int w = 10; int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; /* stack */ int y = 10; /* stack */ static int z; /*.bss */ static int w = 10; int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; /* stack */ int y = 10; /* stack */ static int z; /*.bss */ static int w = 10; /*.data */ int * p; p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; /* stack */ int y = 10; /* stack */ static int z; /*.bss */ static int w = 10; /*.data */ int * p; /* stack */ p = (int *) malloc ( 100 * sizeof (*p) ); free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; /* stack */ int y = 10; /* stack */ static int z; /*.bss */ static int w = 10; /*.data */ int * p; /* stack */ p = (int *) malloc ( 100 * sizeof (*p) ); /* 100 * sizeof(*p) byte alokováno na haldě */ free ( p ); return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Pamět programu Kde se alokují proměnné? int a = 10; /*.data */ int b; /*.bss */ static int c; /*.bss */ int main ( void ) { int x; /* stack */ int y = 10; /* stack */ static int z; /*.bss */ static int w = 10; /*.data */ int * p; /* stack */ p = (int *) malloc ( 100 * sizeof (*p) ); /* 100 * sizeof(*p) byte alokováno na haldě */ free ( p ); /* blok na haldě označen jako volný */ /* halda se může, ale nemusí zmenšit */ return 0; } M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 42/43

Otázky a odpovědi Otázky... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Ukazatele, BI-PA1 43/43