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.



Podobné dokumenty
Distanční opora předmětu: Programování v jazyce C Tématický blok č. 4: Pole a ukazatele Autor: RNDr. Jan Lánský, Ph.D.

Distanční opora předmětu: Programování v jazyce C Tématický blok č. 8: Dynamické datové struktury, ladění programů Autor: RNDr. Jan Lánský, Ph.D.

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

Struktura programu v době běhu

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

Více o konstruktorech a destruktorech

Pole a kolekce. v C#, Javě a C++

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

Ukazatele, dynamická alokace

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

Programování v jazyce C a C++

Distanční opora předmětu: Programování v jazyce C Tématický blok č. 5: Řetězce, parametry programu Autor: RNDr. Jan Lánský, Ph.D.

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

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ý

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

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

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

Maturitní otázky z předmětu PROGRAMOVÁNÍ

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

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

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

ALGORITMIZACE A PROGRAMOVÁNÍ

DUM 06 téma: Tvorba makra pomocí VBA

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

Programovací jazyk Pascal

Základy programování (IZP)

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

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

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

Konstruktory a destruktory

DUM 07 téma: Proměnné, konstanty a pohyb po buňkách ve VBA

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

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

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

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

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

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

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

Znalost výčtových typů. Aktivní znalost kombinovaných (zkrácených přiřazení). Znalost bitových operací. Znalost operátoru sekvence.

2 Datové typy v jazyce C

1. D Y N A M I C K É DAT O V É STRUKTUR Y

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

Vyučovací hodina. 1vyučovací hodina: 2vyučovací hodiny: Opakování z minulé hodiny. Procvičení nové látky

Distanční opora předmětu: Programování v jazyce C Tématický blok č. 2: Proměnná, výraz, příkaz, podmínka, cyklus Autor: RNDr. Jan Lánský, Ph.D.

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

Dynamická alokace paměti

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

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

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

Úvod do programování 6. hodina

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března

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

type Obdelnik = array [1..3, 1..4] of integer; var M: Obdelnik;

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

Paměť počítače. alg2 1

Základy programování (IZP)

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

Odvozené a strukturované typy dat

Preprocesor a koncepce (větších) programů. Úvod do programování 2 Tomáš Kühr

NMIN102 Programování /2 Z, Zk

Algoritmizace a programování

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

Funkční objekty v C++.

přetížení operátorů (o)

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

Martin Flusser. Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague. December 7, 2016

Základy programování (IZP)

Úvod do programování 7. hodina

Distanční opora předmětu: Databázové systémy Tématický blok č. 8: Transact SQL Autor: RNDr. Jan Lánský, Ph.D.

MAXScript výukový kurz

Přetěžování operátorů

Assembler - 5.část. poslední změna této stránky: Zpět

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

Úvod do programování. Lekce 1

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

Mělká a hluboká kopie

Ukazatele a pole. Chceme-li vyplnit celé pole nulami, použijeme prázdný inicializátor: 207 Čárka na konci seznamu inicializátorů

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.

Programujeme v softwaru Statistica

Algoritmizace a programování

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

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

Program převod z desítkové na dvojkovou soustavu: /* Prevod desitkove na binarni */ #include <stdio.h>

02. HODINA. 2.1 Typy souborů a objektů. 2.2 Ovládací prvky Label a TextBox

zapište obslužnou metodu události Click tlačítka a vyzkoušejte chování polevýsledek.text = polečíslo1.text + polečíslo2.text;

MQL4 COURSE. By Coders guru -8- Proměnné

5 Přehled operátorů, příkazy, přetypování

Programování II. Návrh programu I 2018/19

Čtvrtek 3. listopadu. Makra v Excelu. Obecná definice makra: Spouštění makra: Druhy maker, způsoby tvorby a jejich ukládání

Sada 1 - Základy programování

Spojová implementace lineárních datových struktur

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

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

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

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

III/2 Inovace a zkvalitnění výuky prostřednictvím ICT

2 Základní funkce a operátory V této kapitole se seznámíme s použitím funkce printf, probereme základní operátory a uvedeme nejdůležitější funkce.

Funkce pokročilé možnosti. Úvod do programování 2 Tomáš Kühr

konstruktory a destruktory (o)

Transkript:

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. Obsah kapitoly 1 Dynamická alokace paměti 2 Organizace paměti procesu 3 Statické proměnné 4 Struktury 5 Typové konstrukce 5.1 Kombinace typových konstrukcí 5.2 Ukazatel na funkci 5.3 Typedef Studijní cíle Cíle nutné k zahájení studia dalšího tématického bloku Další cíle Znát klíčové pojmy tohoto tématického bloku (alespoň pasivní znalost je podmínkou pro studium dalších bloků). Podle slovního zadání umět napsat a odladit jednoduchý program. Program pracuje se vstupem získaným z parametrů, které byly programu předány z příkazové řádky při jeho zavolání. Použít dynamicky alokovanou paměť, pokud je to pro daný program nezbytné. Umět dynamicky alokovat a uvolnit paměť, umět rozlišit v jakých situacích je dynamická alokace paměti nezbytná. Znalost struktur a přístupu k jejich položkám. Schopnost určit, o jakou typovou konstrukci se jedná, z její definice. Znalost organizace paměti v procesu, včetně určení, co se nachází v jednotlivých částech paměti po spuštění ukázkového zdrojového kódu. Znalost statických proměnných. Znalost ukazatelů na funkce a schopnost je použít. Znalost klíčové slova typedef a schopnost ho použít. Čas potřebný ke studiu 2-4 hodiny na prostudování výukových textů + zodpovězení otázek k rekapitulaci 3-8 hodin na vypracování modelových úloh na PC + POT 3 1-3 hodiny na praktické zopakování učiva na PC ( v jiný den) 30 min - 1 hodina na (znovu)zodpovězení otázek k rekapitulaci (v jiný den) Časy jsou hodně individuální a jsou závislé na míře znalostí z předmětu Úvod do programování a Programování a případných programátorských zkušenostech z jiných jazyků.

Úvod V tomto bloku probereme následující témata. Na ukázkovém příkladu si vysvětlíme k čemu je užitečná dynamicky alokovaná paměť. Vysvětlíme si, jaké jsou rozdíly při práci s dynamicky alokovanou pamětí oproti paměti staticky alokované. Probereme funkce malloc a free. Vysvětlíme si jak je organizována paměť procesu, jaká data se nacházejí v jeho jednotlivých částech. Vysvětlíme si statické proměnné a jejich použití. Probereme deklaraci struktur a přístup k jejich datovým položkám. Zopakujeme si základní typové konstrukce a naučíme se rozpoznávat složitější kombinace typových konstrukcí. Vysvětlíme se ukazatele na funkce a jejich použití. Probereme používání klíčového slova typedef. Výkladová část Vysvětlivky Červený text Porušením nebo opomenutím takto označených pravidel vznikají těžko odladitelné chyby (zejména pro začínající programátory). Modrý text Doporučení jak programovat v praxi. Často prevence závažných chyb. 1 Dynamická alokace paměti Představme si program, do kterého zadáme jako vstupní parametr jméno souboru, ve kterém jsou uložena celá čísla. Na každém řádku je jedno číslo, první řádek udává počet čísel v souboru. Cílem programu je setřídit tato čísla a tuto setříděnou posloupnost čísel zapsat do jiného souboru. Program bude pracovat následujícím způsobem. Nejprve si z prvního řádku zjistí počet čísel v souboru, a poté ze souboru načte jednotlivá čísla do pole. Pole setřídí a čísla uložená v tomto setříděném poli postupně (v pořadí v jakém se nacházejí v poli) uloží do výstupního souboru. V současné době umíme vytvořit pouze staticky alokované pole (tématicky blok č. 4), jehož velikost musí být známa v době kompilace zdrojových kódů programu. Pokud velikost pole není známá v době kompilace, jedná se o syntaktickou chybu, a program nejde zkompilovat. Při psaní programu, bychom museli tedy odhadnout maximální počet čísel, které budeme umět ze souboru načíst a setřídit. Pokud zvolíme velikost pole malou, třeba 100, nebudeme moci naším programem setřídit soubory, které obsahují více než sto čísel. Pokud naopak zvolíme velikost pole velmi velkou, třeba 1 000 000, bude náš program spotřebovávat pro třídění malých souboru, zbytečně velké množství paměti. Navíc pomocí něj nesetřídíme soubor obsahující například 1 000 012 čísel. Naším cílem je využít informaci z prvního řádku souboru, kde je uveden počet čísel v souboru. Chceme vytvořit pole přesně dané velikosti. Statickou alokaci pole nemůžeme

použít, protože tuto velikost neznáme v době kompilace zdrojových kódů programu. Využijeme mechanizmus nazvaný dynamická alokace paměti, který nám umožní vytvořit pole požadované délky za běhu programu. Oproti staticky alokované paměti se u dynamicky alokované paměti musíme starat o několik věcí navíc. Při přidělení dynamicky alokované paměti obdržíme ukazatel na její začátek (první byte). Pokud o tento ukazatel nějakým způsobem přijdeme (například necháme ho ukazovat na naprosto jiný kus paměti) a zároveň se jedná o poslední ukazatel na tuto paměť, stává se tato paměť nedostupná až do konce běhu celého programu, kdy je paměť celého programu operačním systémem uvolněna pro použití jinými programy. Ve chvíli, kdy danou dynamicky alokovanou paměť už nepotřebujeme, měli by jsme ji uvolnit pro další použití v rámci našeho programu. Pro uvolnění paměti potřebujeme opět znát ukazatel na její začátek. Pokud máme ukazatel pouze do jejího prostředku (prováděli jsme na něm třeba operaci ++), při pokusu o uvolnění paměti reprezentované tímto ukazatelem dojde k poškození interních datových struktur programu, ve kterých se uchovávají informace o dynamicky přidělené paměti a při dalším pokusu o novou alokaci paměti může dojít k běhové chybě programu. V jazyku C existují dvě funkce pro práci s dynamicky alokovanou pamětí. První z funkcí malloc nám umožňuje dynamicky alokovat (získat od správce paměti) kus paměti požadované velikosti. Ve chvíli kdy s takto získaným blokem paměti už nechceme dále pracovat, uvolníme ho (vrátíme správci paměti) pomocí funkce free. Obě tyto funkce se nacházejí v knihovně malloc.h, kterou nesmíme zapomenout vložit pomocí direktivy #include. V jazyku C++, který budeme probírat příští semestr, je dynamická alokace řešena pomocí operátorů new a delete. Funkce malloc má pouze jeden parametr typu celé číslo, který udává jak velký kus paměti chceme získat. Pokud chceme dynamicky alokovat například pole datového typu int o velikosti 20 prvků, musíme nejprve spočítat jeho předpokládanou velikost a o takto velký kus paměti požádat pomocí funkce malloc. Velikost datového typu zjistíme pomocí operátoru sizeof. Jak víme z tématického bloku č. 3, velikost datových typů se může měnit v závislosti na typu překladače. Jedinou výjimkou je datový typ char, u něhož si můžeme být jistí jeho velikostí 1 byte. Velikost pole získáme vynásobením velikosti datového typu jednotlivého prvku pole s počtem prvků v poli. Na slajdu č. 76 napravo nahoře vidíme zdrojový kód pro dynamickou alokaci pole 20 prvků typu int. Funkce malloc vrací návratovou hodnotu typu void *, ukazatel na kus paměti, která nemá svůj typ. V jazyku C při použití ukazatele na typ void na pravé straně přiřazovacího příkazu se tento ukazatel automaticky přetypuje na ukazatel na konkrétní datový typ daný proměnnou na levé straně přiřazení. Protože však používáme kompilátor jazyka C++, ve kterém se tato automatická konverze neprovádí, musíme provést přetypování explicitně, jak je znázorněno v bublině uprostřed slajdu č. 76. Vrácená dynamicky alokovaná paměť je vždy neinicializovaná, obsahuje neznámé náhodné hodnoty. Návratovou hodnotou funkce malloc je ukazatel na dynamicky alokovaný kus paměti. Dostupná paměť však není neomezená, a může se nám stát, že jsme již veškerou paměť určenou operačním systémem pro náš program vyčerpali a další nám nebude přidělena. Představme si cyklus ve kterém opakovaně požadujeme paměť o velikosti 1 GB. V případě, že se alokace nepovedla, a paměť nám nebyla přidělena, je návratovou hodnotou nulový ukazatel. Po každém zavolání funkce malloc bychom měli testovat jí vrácený ukazatel na jeho nenulovost. V případě vrácení nulového ukazatele se můžeme pokusit uvolnit některou

nepoužívanou dynamicky alokovanou paměť a poté se znovu pokusit o opětovnou alokaci. Nejčastějším řešením této situace u menších aplikací bývá vypsáním chybové hlášky o nedostatku paměti na obrazovku a ukončení programu. Funkce free slouží k uvolnění dynamicky alokované paměti. Funkce nemá žádnou návratovou hodnotu a má jeden parametr typu ukazatel na typ void. Ukazatel na libovolný datový typ se na automaticky na přetypuje na ukazatel na typ void. Každou dynamicky alokovanou paměť (pomocí funkce malloc), kterou již nechceme používat bychom měli uvolnit pomocí funkce free. K uvolnění paměti by mělo dojít i těsně před skončením programu, zejména z kontrolních důvodů. Při uvolňování paměti se mohou projevit chyby, které jsem v programu způsobili někdy dříve (například přepsání místa v paměti těsně za koncem polem). Na slajdu č. 76 dole vidíme zdrojový kód ve které dynamicky alokujeme paměť velikosti 20 bytů, kterou budeme chápat jako pole znaků. Na třetím řádku zdrojového kódu si všimněme testu na nulovost ukazatele. Pokud je ukazatel nulový, zavolá se uživatelsky definovaná funkce error (její tělo je nutno dopsat). S dynamicky alokovanou pamětí lze dále pracovat stejně jako se staticky alokovanou. Na čtvrtém řádku do ní okopírujeme řetězec "ahoj" a na pátém řádku první znak tohoto řetězce přepíšeme na hodnotu 'X'. V příkladu chybí uvolnění paměti pomocí funkce free. Příklad je graficky znázorněn na slajdu vpravo dole. Na slajdu č. 77 vidíme další příklad na využití dynamicky alokované paměti. Zatímco minulý příklad by šel přepsat i s použitím pouze statických alokací, tento již nejde. V příkladu si vytvoříme pole přesně takové délky, abychom do něj mohli zřetězit první a druhý parametr programu, předané mu z příkazové řádky při jeho spuštění. Jedná se o parametry s indexy 1 a 2, protože parametr s indexem 0 je název programu včetně plné cesty. Po dynamické alokaci paměti provedeme test na nulovost ukazatele na právě alokovanou paměť. Následně se do dynamicky alokované paměti nakopíruje první parametr programu předaný z příkazové řádky a za něj se připojí druhý parametr. Ve zdrojovém kódu opět chybí uvolňování paměti pomocí funkce free. 2 Organizace paměti procesu Po zkompilování zdrojových kódů získáme program. Program můžeme spustit. Z hlediska operačního systému se běžící program nazývá proces. V našich studijních materiálech jsme zatím tyto pojmy nerozlišovaly a oba dva nazývali programem, případně proces nazývali běžícím programem. Každý proces má svoji paměť, kterou má přidělenou od operačního systému, ve které jsou uchovány všechny data nutná pro běh procesu. Ostatní procesy operačního systému nemohou z této paměti číst ani do ní zapisovat. Paměť je přidělena při startu procesu, ale v jeho průběhu se může její velikost zvětšovat. Při ukončení procesu je všechna paměť vrácena opět operačnímu systému. Tato paměť se dělí na čtyři části: kódový segment, datový segment, heap a zásobník. Na slajdu č. 78 vidíme jejich vzájemné uspořádání. Kódový a datový segment mají pevnou velikost danou už při startu programu. Zásobník a heap mají proměnlivou velikost, soupeří spolu o stejnou volnou paměť, rostou proti sobě. Kódový segment (slajd č. 79) je připraven kompilátorem při překladu zdrojových kódů programu a je součástí spustitelného souboru. Je zde uložen (přeložený) kód uživatelských a knihovních funkcí. Tato část paměti (na rozdíl od zbylých tří částí paměti) bývá obvykle

chráněna proti zápisu. Pokud je kódový segment chráněn proti zápisu a nějaký neinicializovaný ukazatel náhodou ukazuje na data z kódového segmentu, při pokusu o přepsání těchto dat pomocí tohoto ukazatele dojde k běhové chybě programu. Kódový segment (slajd č. 80) je (stejně jako kódový segment) připraven kompilátorem při překladu zdrojových kódů programu a je součástí spustitelného souboru. Jsou zde uloženy explicitně nebo implicitně (nulami) inicializované globální proměnné. Dále se zde nachází řetězcové konstanty použité kdekoliv v programu. Nachází se zde i data knihoven. V příkladu uvedeném dole na slajdu se v datovém segmentu nachází globální proměnná bigpole a řetězcová konstanta "ahoj". Heap (slajd č. 81), neboli halda, je vytvořen startovacím modulem knihoven. Z této části paměti se přidělují kusy paměti při dynamické alokaci, volná paměť je spravována pomocí spojového seznamu. Po zavolání funkce malloc je z této části paměti přidělen blok paměti požadované velikosti (ve spojovém seznamu se najde volný blok stejné nebo větší velikosti) a ukazatel na jeho první byte je vrácen jako návratová hodnota funkce malloc. Po zavolání funkce free je tento blok připojen do seznamu volných bloků paměti, případně může navíc dojít ke sloučení sousedních volných bloků. Zásobník (slajd č. 82), neboli stack, je připraven operačním systémem. Ukládají se zde aktivační záznamy funkcí, které obsahují parametry funkce, návratovou hodnotu, hodnoty lokálních proměnných, včetně pomocných proměnných generovaných kompilátorem. Dále se zde ukládají návratové adresy (ukazatel do kódu volající funkce, kam se máme vrátit po skončení volané funkce). V příkladu uvedeném dole na slajdu se v zásobníku nachází lokální proměnné pole, s a x. Dále jsou zde dočasné proměnné, které kompilátor vyrobí pro spočtení výrazu 1+2*3. 3 Statické proměnné Statickou proměnnou deklarujeme uvedením klíčového slova static před nebo za datový typ proměnné. Je velký rozdíl mezi tím, zda toto slovo použijeme u globální proměnné nebo u lokální proměnné. Statická globální proměnná není viditelná z jiných modulů, můžeme v nich mít globální proměnnou stejného jména. Lokální statická proměnná se chová jako (nestatická) globální proměnná (je uložena v datovém segmentu) s tím rozdílem, že lokální statická proměnná je viditelná pouze z funkce, ve které je lokální proměnnou. Hodnota lokální statické proměnné zůstává zachována i po skončení funkce, ve které je tato statická lokální proměnná definována. Po opětovném zavolání této funkce má tato statická lokální proměnná stejnou hodnotu, jakou měla po skončení předchozího volání této funkce. Pokud je lokální statická proměnná inicializována na konkrétní hodnotu, tato inicializace se provádí pouze jednou (v jazyku C před vstupem do funkce main, v jazyku C++ před prvním průchodem funkce, ve které se tato proměnná nachází). Lokální statické proměnné se občas používají jako čítače počtů zavolání dané funkce. Na slajdu č. 83 je zdrojový kód s globální statickou proměnnou x a lokální statickou proměnnou y.

4 Struktury Struktura je složený datový typ, který se používá k reprezentaci více spolu souvisejících prvků (položek struktury), které mohou být různých datových typů. Struktura se definuje pomocí klíčového slova struct následovaného názvem struktury a složenými závorkami, ve kterých jsou středníky oddělené jednotlivé dvojice datový typ a název položky struktury. Za definicí struktury následuje vždy středník. Na slajdu č. 85 je definována struktura osoba, které má čtyři položky jmeno, prijmeni, rok_narozeni a pohlavi. Na prvním řádku pod definicí struktury deklarujeme tři proměnné. Proměnnou os typu osoba, proměnnou po typu ukazatel na osobu a pole osob zam. Na dalším řádku deklarujeme proměnnou beda typu osoba a inicializujeme jí ve složených závorkách uzavřeným seznamem hodnot jednotlivých položek navzájem oddělených čárkami. Obdobné jako lze inicializovat pole. K jednotlivým položkám struktury přistupujeme pomocí operátoru tečky. Na 3. řádku pod definicí struktury nakopírujeme do položky jmeno u proměnné os hodnotu "Venca". Na dalším řádku prvku s indexem 3 v poli zam do položky rok_narozeni přiřadíme hodnotu 1968. Následně do ukazatele po uložíme adresu prvku s indexem 3 v poli zam. Na dalším řádku do položky pohlavi u proměnné typu osoba, na kterou ukazuje ukazatel po uložíme hodnotu 1. Použijeme operátor ->, který přistoupí k položce struktury na kterou ukazuje ukazatel. Zápis po->pohlavi je nahrazením nepohodlného zápisu (*po).pohlavi, který obsahuje dva operátory a jedny závorky. Závorka je v zápisu (*po).pohlavi nutná, protože postfixní operátory mají vyšší prioritu než prefixní operátory a my chceme provést nejdříve dereferenci a až poté přístup k položce. 5 Typové konstrukce Na slajdu č. 86 vidíme v tabulce přehled základních typových konstrukcí, které by jsme měli již znát. Ignorovat můžeme modré řádky, což nejsou konstrukce z jazyka C, ale z jazyka C++. Písmeno A zastupuje datový typ, písmeno x jméno identifikátoru (proměnné, funkce). U některých konstrukcí jsou červeně zobrazeny operace, které s nimi nelze provádět, zeleně jsou naopak zobrazeny operace, které s nimi provádět lze. První dva řádky ukazují různé způsoby definice pole. Definici pole bez udání jeho velikosti uvedená na druhém řádku lze použít jen v některých případech, například když po ní následuje inicializace pole. Na třetím řádku je definován ukazatel na konkrétní datový typ, na čtvrtém řádku ukazatel na neručený datový typ. Na pátém řádku jsou dva možné zápisy ukazatele na konstantní hodnotu daného typu. Při deklaraci je důležité, že slovo const se nachází před hvězdičkou. Nemůžeme měnit hodnotu, na kterou ukazatel ukazuje, můžeme ji pouze číst. Tento typ ukazatele má své využití například u funkcí pro práci s řetězci, které nemají modifikovat své parametry. Na šestém řádku je konstantní ukazatel. Při deklaraci je důležité, že slovo const se nachází za hvězdičkou. Můžeme měnit hodnotu, na kterou ukazatel ukazuje, ale nemůžeme měnit místo na které ukazatel ukazuje. Bude vždy ukazovat na to samé místo. Pokud se ve zdrojovém kódu vyskytne pole na pozici, na které nemůže být, automaticky se přetypovává na ukazatel na první prvek. Takto vzniklý ukazatel je konstantní.

Můžeme vytvořit i konstantní ukazatel na konstantní hodnotu, ale kvůli jeho nepraktičnosti pro běžném programovaní není v tabulce uveden. Pro jeho deklaraci stačí uvést slovo const jednou před a jednou za hvězdičku. Na posledních čtyřech řádcích jsou různé varianty funkcí, bez parametrů, s parametry, bez návratové hodnoty. 5.1 Kombinace typových konstrukcí Na slajdu č. 87 vidíme kombinaci jednoduchých typových konstrukcí z předchozího slajdu. Písmeno A zastupuje datový typ, písmeno x jméno identifikátoru (proměnné, funkce). Červené označené řádky jsou syntakticky zakázané. Fialové řádky jsou povolené, ale nepoužívají se, protože pole se automaticky přetypovává na ukazatel na jeho první prvek. Podrobně si vysvětlíme pouze černé řádky, které se v programech běžně používají. Porozumět některým složitějším zápisům může být na první pohled těžké. Vysvětlíme si jednoduché pravidlo jak takovéto zápisy číst. V zápisu najdeme identifikátor (v našem případě x) a od něj postupně směrem doprava čteme typové konstrukce. Když vyčerpáme všechny konstrukce napravo od identifikátoru, tak postupujeme od identifikátoru směrem doleva. Situaci mohou zkomplikovat kulaté závorky určující prioritu typových konstrukcí (pozor na záměnu s voláním funkce). Výraz v závorkách se vyhodnotí přednostněji. Na prvním řádku je pole ukazatelů. Zkusíme zápis přečíst pomocí našeho pravidla. Nalezneme identifikátor x a napravo od něj se nacházejí hranaté závorky obsahující hodnotu 10. Identifikátor x je pole desíti prvků. Napravo se již žádné konstrukce nenacházejí, postupujeme tedy od x směrem doleva. První nalevo je hvězdička, tedy ukazatel. Identifikátor x je pole desíti ukazatelů. Před hvězdičkou je A, což je nějaký typ. Identifikátor x je pole desíti ukazatelů na typ A. Na třetím řádku je funkce vracející ukazatel. Zkusíme zápis přečíst pomocí našeho pravidla. Nalezneme identifikátor x a napravo od něj se nacházejí kulaté závorky, které by mohli případně obsahovat i parametry oddělené čárkou. Identifikátor x je funkce. Napravo se již žádné konstrukce nenacházejí, postupujeme tedy od x směrem doleva. První nalevo je hvězdička, tedy ukazatel. Identifikátor x je funkce vracející ukazatel. Před hvězdičkou je A, což je nějaký typ. Identifikátor x je funkce vracející ukazatel na typ A. Na čtvrtém řádku je ukazatel na funkci. Oproti zápis z předchozího řádku zde přibyli pouze jedny kulaté závorky určující prioritu jednotlivých konstrukcí. Zkusíme zápis přečíst pomocí našeho pravidla. Nalezneme identifikátor x a napravo od něj se nachází pravá kulatá závorka, pravou stranu jsme dočasně vyčerpali. Postupujeme od x směrem doleva. První nalevo je hvězdička, tedy ukazatel. Identifikátor x je ukazatel. Další nalevo je levá kulatá závorka párová k pravé kulaté závorce, na kterou jsme již narazili. Postup nalevo dočasně zastavíme. Postupujeme od pravé kulaté závorky (u které jsme před chvílí přerušili náš postup směrem napravo) směrem napravo. Nalezneme kulaté závorky, které by mohli případně obsahovat i parametry oddělené čárkou. Identifikátor x je ukazatel na funkci. Napravo se již žádné konstrukce nenacházejí, postupujeme směrem nalevo od posledního místa, kde jsme nalevo skončili. Nachází se zde A, což je nějaký typ. Identifikátor x je ukazatel na funkci vracející typ A.

Na šestém řádku je pole ukazatelů na funkci. Zkusíme zápis přečíst pomocí našeho pravidla. Nalezneme identifikátor x a napravo od něj se nacházejí hranaté závorky obsahující hodnotu 10. Identifikátor x je pole desíti prvků. Dále napravo se nachází pravá kulatá závorka, pravou stranu jsme dočasně vyčerpali. Postupujeme od x směrem doleva. První nalevo je hvězdička, tedy ukazatel. Identifikátor x je pole desíti ukazatelů. Další nalevo je levá kulatá závorka párová k pravé kulaté závorce, na kterou jsme již narazili. Postup nalevo dočasně zastavíme. Postupujeme od pravé kulaté závorky (u které jsme před chvílí přerušili náš postup směrem napravo) směrem napravo. Nalezneme kulaté závorky, které by mohli případně obsahovat i parametry oddělené čárkou. Identifikátor x je pole desíti ukazatelů na funkci. Napravo se již žádné konstrukce nenacházejí, postupujeme směrem nalevo od posledního místa, kde jsme nalevo skončili. Nachází se zde A, což je nějaký typ.. Identifikátor x je pole desíti ukazatelů na funkci vracející typ A. 5.2 Ukazatel na funkci Ukazatel na funkci je druh ukazatele, který obsahuje adresu (z kódového segmentu) nějaké funkce. Pomocí tohoto ukazatele můžeme funkci, na kterou ukazuje zavolat (musíme ji předat požadované parametry). Jak deklarovat ukazatele na funkci jsme si představili na slajdu č. 87 na 4. řádku. Nyní se tuto problematiku ukážeme na praktickém příkladu. Například zápis int (* fce) (int a, int b) deklaruje ukazatel na funkci fce, která má dva parametry typu int a má návratovou hodnotu typu int. Mějme v programu dvě funkce secti a odecti, které mají oba dva parametry typu int a návratovou hodnotu také typu int. Do proměnné fce můžeme přiřadit buďto hodnotu secti nebo odecti. Použít lze jak zápis fce=secti tak i fce=&secti. Pokud x je proměnná typu celé číslo, mohu do ní uložit výsledek volání funkce fce pomocí zápisu x=fce(2,5), zavolá se funkce secti na kterou ukazatel fce ukazuje a výsledkem bude číslo 7. Nyní si vysvětlíme, k čemu mohou být ukazatelé na funkci užitečné. Představme si, že kromě funkcí secti a odecti máme v programu ještě deset dalších funkcí se stejnými parametry a návratovou hodnotou. Na začátku programu si jednu z těchto funkcí zvolíme, například podle vstupu od uživatele. Tuto zvolenou funkci aplikujeme v programu na více místech. Pokud použijeme ukazatele na funkce, problém, jak převést uživatelův požadavek na konkrétní funkci, řešíme pouze jednou na začátku programu. Dále v programu se volá funkce pomocí proměnné, která obsahuje ukazatel na ni. Pokud ukazatele na funkce nepoužijeme, musíme řešit převod požadavku uživatele na danou funkci na každém místě programu znova. 5.3 Typedef Pomocí konstrukce typedef můžeme zpřehlednit zdrojový kód, pokud se v něm opakovaně vyskytují složitější typové konstrukce. Rovněž ho lze použít pro vytvoření vlastním názvů pro standardní datové typy. Například zápisem typedef int delka definujeme datový typ delka, který bude odpovídat typu int. Pokud se v tomto případě rozhodneme časem, že typ delka by měl odpovídat typu long, stačí změnu provést na jednom místě v programu. Příklad použití pro ukazatele na funkce nalezneme na slajdu č. 88 dole.

Klíčové pojmy dynamicky alokovaná paměť (malloc a free) kódový segment, datový segment, heap, zásobník. aktivační záznam funkce statické proměnné (globální a lokální) pole ukazatelů, funkce vracející ukazatel, ukazatel na funkci, pole ukazatelů na funkci typedef Otázky k rekapitulaci Upozornění: odpovědi na některé zde uvedené otázky nelze najít ve studijním textu tohoto tématického bloku. Lze je získat vlastním experimentováním se zdrojovými kódy nebo studiem doporučené literatury. V jakých situacích si nevystačíme se staticky alokovanou pamětí a musíme použít paměť dynamicky alokovanou. Pomocí jaké funkce získáme dynamicky alokovanou paměť? Jaká má tato funkce parametry, návratovou hodnotu? Musí se dynamická alokace pokaždé úspěšně provést? Jak se tato situace řeší? Z jakého důvodu je vhodné dynamicky alokovanou paměť uvolňovat a pomocí jaké funkce ho provádíme? Jaký je rozdíl mezi programem a procesem? Na jaké části dělíme paměť procesu? Jaká data se v jednotlivých částech nacházejí? Co obsahuje aktivační záznam funkce? Na slajdu č. 84 je kus zdrojového kódu. Jaká data se budou nacházet v jednotlivých částech paměti procesu (kódový segment, datový segment, heap, zásobník) při spuštění a běhu procesu vzniklého spuštěním tohoto zkompilovaného zdrojového kódů? Co je to globální statické proměnné? Co je to lokální statická proměnná? Jak se liší od normální lokální proměnné a jak se liší od globální proměnné? K čemu jsou užitečné struktury a jak se definují? Na co nesmíme zapomenout při definici struktury? Dají neproměnné typu struktura inicializovat při deklaraci? K čemu slouží operátor ->, jak ho lze nahradit? Jaké základní typové konstrukce známe? Jaké se používá pravidlo pro čtení typových konstrukcí? Jaké kombinace základních typových konstrukcí jsou běžně používané? Co je to ukazatel na funkci a k čemu je užitečný? Co znamená zápis na slajdech č. 88 a 89? K čemu se používá klíčové slovo typedef? Své odpovědi zdůvodněte. Můžete přidat i syntaktické zápisy tam, kde je to vhodné.

Doporučené příklady k naprogramování 1. Napište funkci, která dynamicky alokuje prostor pro matici celých čísel n*m. Nápověda: Výsledná struktura vypadá podobně jako argv. 2. Napište funkce, které spočtou nejmenší společný násobek (nsn) a největší společný dělitel (nsd) dvou čísel. Nápověda: použijte Euklidův algoritmus. 3. Napište funkci, která jako své parametry má matici M a její rozměry a ukazatel na funkci F se dvěmi parametry typu int vracející int. Do každého prvku matice M bude uložen výsledek funkce F. Funkce je volána s parametry x a y, které určují pozice daného prvku v matici.. 4. Napište program s parametry "{-nsn -nsd } [-f output] n [m]", který spočte nsd nebo nsn pro všechny uspořádané dvojice čísel [x,y] takových že x je z 1..n a y je z 1..m. Pokud m není zadáno, bude mít stejnou hodnotu jako n. Výsledky těchto výpočtů se ukládají do matice získané z příkladu č.1. pomocí funkce z příkladu č.3. 5. Obsah matice z příkladu č. 4 bude vypsán na obrazovku 6. Obsah matice z příkladu č. 4 bude vypsán do souboru, pokud je soubor zadán pomocí -f. Nápověda: zápis do souboru se probírá v tématickém bloku č. 7. Studijní literatura Výklad často odkazuje na slajdy (ve formátu.ppt), které je vhodné si vytisknout. Je vhodné si pořídit nějakou knihu o programování v C nebo C++. Uvedené příklady knih berte pouze jako inspirativní. Miroslav Virius: Programování v C++ (ČVUT, 2. vydání 2004) Jesse Liberty, Bradley L. Jones: Naučte se C++ za 21 dní (Computer Press, 2. vydání, 2007) Knihu je dobré číst postupně a vlastním tempem, můžete mít i mírné zpoždění oproti našemu výkladu. Pořadí kapitol v knize neodpovídá úplně přesně pořadí, v jakém učivo probíráme. Tento tématický blok se zaměřte na dynamicky alokovanou paměť, struktury, ukazatele na funkce. Miroslav Virius: Pasti a propasti jazyka C++ (Brno, 2. vydání 2005) Kapitola 11 Správa paměti (Je psána pro C++, ale principy jsou podobné jako v C.)