Pednky KAEPEL Programovn v elektronice Katedra aplikovan elektroniky

  • Slides: 79
Download presentation
Přednášky KAE/PEL Programování v elektronice Katedra aplikované elektroniky a telekomunikací přednášky: Ing. Jiří Basl,

Přednášky KAE/PEL Programování v elektronice Katedra aplikované elektroniky a telekomunikací přednášky: Ing. Jiří Basl, Ph. D. basl@kae. zcu. cz EK 502 cvičení: Ing. Petr Weissar, Ph. D. weissar@kae. zcu. cz EK 515 Ing. Jiří Basl, Ph. D. basl@kae. zcu. cz EK 502 Ing. Jakub Vlášek vlasekj@kae. zcu. cz EL 512

Vývojové prostředí a ukládání • Nabídka START • Na počítačích v laboratoři doporučeno "vytáhnout

Vývojové prostředí a ukládání • Nabídka START • Na počítačích v laboratoři doporučeno "vytáhnout na plochu" • Ukládání do domácího adresáře H: • Alternativně (ve starém VS 2005 nutně) možno "lokálně" na D: homelogin_name – po odhlášení není zaručeno zachování obsahu • Aktuální podklady - http: //portal. zcu. cz – Courseware – KAE/PEL

Optimalizace prostředí

Optimalizace prostředí

Vytvoření nového programu • File/New/Project. . . – – – C++ Projects Win 32

Vytvoření nového programu • File/New/Project. . . – – – C++ Projects Win 32 Console App. Location – H: . . . Name – jméno programu • App. settings – Console • MFC – nebudeme používat

Základní kostra programu // xxx. cpp : Defines the entry point for the console

Základní kostra programu // xxx. cpp : Defines the entry point for the console application. // #include "stdafx. h" int _tmain(int argc, _TCHAR* argv[]) { return 0; } • Hlavní soubor má příponu CPP – překladač je defaultně nastaven jako C++, ale kromě několika drobných rozdílů je stejný i pro C • Příkaz include patří preprocesoru (pozná se podle # na začátku) • připojuje (vkládá) jiný soubor do aktuálního • "soubor" – bere se napřed z aktuálního, pak ze systémového • <soubor> - napřed ze systémového, pak z aktuálního adresáře • Hlavička funkce main zde odpovídá konvencím Windows, jinak bývá: • int main(void) • int main(int argc, char** argv) • Tělo funkce zatím prázdné

Spouštění aplikací Nejlépe přímo z prostředí: • Debug/Start without. . . (CTRL+F 5) –

Spouštění aplikací Nejlépe přímo z prostředí: • Debug/Start without. . . (CTRL+F 5) – spustí okno, vygeneruje "Press any. . . " • Debug/Start (F 5) • Alternativně tlačítka na liště "Debug" • Pokud je zdroj novější než již přeložený EXE (byly provedeny úpravy), objeví se dotaz na Build (nové sestavení programu) – odpovědět ANO ! • Pokud dojde k chybám překladu, objeví se dotaz, zda přesto spustit (starší verzi) – odpovědět NE ! • Úspěšně přeložené EXE možno spustit přímo v OS – soubor se nachází v adresáři projekt/bin/Debug • Při spuštění bez debuggeru je možno se připojit k běžícímu programu pomocí menu Debug/Attach – možno dát breakpointy a krokovat

Přehled vytvořených souborů • Wizard vytvořil zdrojové soubory • Solution Explorer: • stdafx. h

Přehled vytvořených souborů • Wizard vytvořil zdrojové soubory • Solution Explorer: • stdafx. h obsahuje #include běžných standardních knihoven, případné další doplňovat sem, budou použitelné pro ostatní moduly • stdafx. cpp je prakticky prázdná, umožňuje průvodům prostředí (Wizard) přidávat další kód

První program – debuger • Moderní vývojové nástroje typicky umožňují sledování běhu programu •

První program – debuger • Moderní vývojové nástroje typicky umožňují sledování běhu programu • Ovládání klávesovými zkratkami nebo myší • Pomocná okna – sledování hodnot proměnných – možnost změny obsahu proměnných za chodu – krokování, zastavení a spuštění kódu – breakpoint - F 9 nebo kliknutí myší vedle "řádků" – krokování – F 10 (po funkcích), F 11 (do funkce) – pokračování vykonávání (nebo spuštění) – F 5 – Call Stack – aktuální funkce a hierarchie vnoření/volání – Command Window – možno psát příkazy • lze provádět výpočty • vypisovat hodnoty i výrazy ("příkaz" ? ) • • – Locals – lokální proměnné v aktuální funkci – Watch – nastavitelné proměnné pro sledování – Autos – proměnné, aktuální v daném kontextu Výpis obsahu proměnné – počkat myší nad proměnnou V Locals apod. možno změnit hodnotu a ovlivnit/testovat chování programu

Debuger – nabídka oken • Jednotlivá okna se mohou dle potřeby dokovat nebo automaticky

Debuger – nabídka oken • Jednotlivá okna se mohou dle potřeby dokovat nebo automaticky skrývat • Je možné okna seskupovat • V novějších verzích VS je možné "vytáhnout" okno mimo prostředí – často používané při více monitorech

Debuger – příklad oken

Debuger – příklad oken

Více projektů v solution • V rámci solution je možné mít více projektů –

Více projektů v solution • V rámci solution je možné mít více projektů – v případě Express edice VS pouze stejného typu – v případě Professional a lepší je možné je míchat libovolně – pravé myšítko – Add/New project • Nutno zvolit, který projekt se má spouštět – jak pro F 5 (s debug), tak pro Ctrl+F 5 – nastavit vlastnost projektu "Set as Startup Project" • pravé myšítko na projektu • nebo menu Project

Číselné soustavy • Procesory vnitřně počítají v binární logice • Veškeré ostatní formáty jsou

Číselné soustavy • Procesory vnitřně počítají v binární logice • Veškeré ostatní formáty jsou „jen pro člověka“ • Zadávání čísel ve zdrojovém kódu – – dekadická čísla – „normální zápis“, číselný základ 10 hexadecimální – číselný základ 16, prefix 0 x. . osmičkové – číselný základ 8, prefix 0 – může být matoucí !! binární – neexistuje • Zobrazení čísel – vlastní kód – printf a deriváty • používají formátovací povely (uvozené %) • "zvládnou" pouze základní datové typy

Hexadecimální, dekadická, oktalová a binární soustava • Hexa Deka Oktal Bin 0 0000 8

Hexadecimální, dekadická, oktalová a binární soustava • Hexa Deka Oktal Bin 0 0000 8 8 10 1000 1 1 1 0001 9 9 11 1001 2 2 2 0010 A 10 12 1010 3 3 3 0011 B 11 13 1011 4 4 4 0100 C 12 14 1100 5 5 5 0101 D 13 15 1101 6 6 6 0110 E 14 16 1110 7 7 7 0111 F 15 17 1111 Výhoda ne-dekadických reprezentací – – • • jedné oktalové číslici odpovídají 3 bity jedné hexadecimální číslici odpovídají 4 bity char a byte lze zapsat pomocí PRÁVĚ 2 hexadecimálních číslic 16 -bitové int a word (unsigned int) pomocí 4 číslic (hexa)

Základní datové typy • Celočíselné – int, long int, short int, unsigned long int,

Základní datové typy • Celočíselné – int, long int, short int, unsigned long int, long, usigned long. . . – char, unsigned char, signed char • Desetinné – float, double, (long double) • Ukazatele (pointery) • Další – pole, struktura, union – řetězce (pomocí ukazatelů

Rozsahy hodnot celých čísel • Rozsahy vychází z vnitřní reprezentace číselných hodnot – vycházejí

Rozsahy hodnot celých čísel • Rozsahy vychází z vnitřní reprezentace číselných hodnot – vycházejí z 2 n • Jedná se o minimální zaručené hodnoty podle standardu C/C++ – mohou být i „větší“ (obsazenou pamětí) – garantováno pořadí velikostí ( char ≤ int ≤ long ) void Speciální typ = „žádný“ – pro funkce char 8 bitů -128 ÷ 127 (-28/2 ÷ 28/2 -1 ) unsigned char (byte) 8 bitů 0 ÷ 255 ( 0 ÷ 28 -1 ) int (short) pro 8 - a 16 -bit 16 bitů -32 768 ÷ 32 767 unsigned int (word) (malé) 16 bitů 0 ÷ 65 535 int (short) pro 32 bit (Win) 32 bitů -2 147 483 648 ÷ 2 147 483 647 unsigned int (word) (Win) 32 bitů 4 294 967 295 (232 - 1) long 32 bitů -2 147 483 648 ÷ 2 147 483 647 unsigned long 32 bitů 4 294 967 295 (232 - 1) float 32 bitů Desetinné číslo

 • Celočíselné – 125 -2516 +10 - celočíselné konstanty desítkové – 0177 010

• Celočíselné – 125 -2516 +10 - celočíselné konstanty desítkové – 0177 010 024 - celočíselné konstanty osmičkové – 0 x 10 0 x 3 E 8 0 x 64 0 x. FFFF. . . – celočíselné konstanty hexadecimální • Znakové (mají celočíselnou hodnotu danou pozicí v ASCII tabulce) – ‘A’ ‘b’ ‘ 0’ ‘ 9’ – ‘/’ ‘*’ ‘@’ ‘ ‘ ‘n’ ‘\’ ‘ x. A‘ ‘ x 61‘ • Desetinné – 1. 2 -3. 15 1. 1 E 3 -2. 14 E 5 1. 2 E-4 Konstanty • Řetězcové (uzavřené v uvozovkách) – “AHOJ” “A” “ 0” “*” “” – “n. Delsi retez s odradkovanimn” “C: \TEST\DATA. TXT” • Ukazatel (bude vysvětleno později) – NULL • Chybné konstanty – – – 018 1 A 2 0 x 2 DCG ‘ 12’ ‘AHOJ’ ‘’ 1, 2 1+2 (syntakticky správně, není konstanta, je to výraz)

Přednáška 2 • • • Operátory Cykly Podmínky Pre/post-inkrement Větvení Problematika signed/unsigned typů

Přednáška 2 • • • Operátory Cykly Podmínky Pre/post-inkrement Větvení Problematika signed/unsigned typů

Aritmetické operátory • Pro 2 operandy: § + - sčítání § - - odčítání

Aritmetické operátory • Pro 2 operandy: § + - sčítání § - - odčítání § * - násobení § pozor má také další význam, bude probrán později § / - dělení § % - zbytek po dělení (modulo), jen pro celá čísla • Pro jeden operand (unární) § - - unární mínus

Další operátory • Přiřazovací: § = - základní přiřazovací operátor § += - rozšířený

Další operátory • Přiřazovací: § = - základní přiřazovací operátor § += - rozšířený přiřazovací operátor • zápis a += 5 odpovídá a = a + 5 § § -= *= /= (a další podobné) • Inkrementace a dekrementace (jen pro celá čísla): § § ++prom - inkrementace před použitím prom++ - inkrementace po použití --prom - dekrementace před použitím prom-- - dekrementace po použití

Další operátory 2 • Porovnávání § § § • == - rovno != -

Další operátory 2 • Porovnávání § § § • == - rovno != - nerovno > - větší než >= - větší nebo rovno < - menší než <= - menší nebo rovno Logické operátory § ! - logická negace – unární operátor § && - logický součin § || - logický součet • Práce s ukazateli § * - hodnota na adrese (dereference) – unární operátor § & - adresa proměnné (reference) – unární operátor § -> - hodnota položky struktury na adrese

Další operátory 3 • Bitové operace § § § >> - bitový posun doprava

Další operátory 3 • Bitové operace § § § >> - bitový posun doprava << - bitový posun doleva & - bitový součin | - bitový součet ^ - bitové xor (exklusivní součet) ~ - bitová negace – unární operátor • Ternární operátor – jednoduché podmínkové výrazy if. . else. . lze zkrátit pomocí ternárního operátoru ? : následovně • • • před ? je podmínka (vyhodnocení splněno/nesplněno) mezi ? a : je hodnota nebo výraz použitý pro "splněno" za : je hodnota nebo výraz pro "nesplněno" hodnota může být libovolný výraz, oba nutně stejného typu používají se jak konstanty, tak opravdu výpočty prom = (podminka) ? vyraz_1 : vyraz_2; if (podmínka) { prom = vyraz_1; } else { prom = vyraz_2; }

. . . if (podminka) akce_pri_splneni; . . . Podmínky . . . if

. . . if (podminka) akce_pri_splneni; . . . Podmínky . . . if (podminka) { blok prikazu }. . . if (podminka 1) akce_pri_splneni_1; else // pri nesplneni je to slozitejsi if (podminka 2) akce_pri_splneni_2; else akce_pri_nesplneni_2; . . . if (podminka) akce_pri_splneni; else akce_pri_nesplneni; . . . if (podminka) { blok prikazu pri splneni } else akce_pri_nesplneni; . . . if (podminka) ; // pri splneni se neudela nic else akce_pri_nesplneni; . . . if (podmínka_1) // pri splneni slozitejsi if (podminka 2) akce_pri_splneni_2; else CHYBA !!!!!, tak to nebylo myšleno akce_pri_nesplneni_1; . . . • else se vždy váže na "bližší" if – nutno závorkovat bloky • Praktické doporučení – pokud kód podmínky není triviální, je vhodné dávat příkaz do bloku – – optické oddělení jasně sdělení úmyslů překladači

Vyhodnocení podmínek • Podmínky obecně se vyhodnocují na 2 stavy: – splněno (true) –

Vyhodnocení podmínek • Podmínky obecně se vyhodnocují na 2 stavy: – splněno (true) – odpovídá nenulová hodnota – nesplněno (false) – odpovídá nulová hodnota • Výhoda – není třeba testovat nenulovost • Někdy se test uvádí pro zvýšení čitelnosti, překladač jej často vypustí – doporučeno např. pro bitové a logické operace AND a OR . . . int i = 0 x 0 f; // hodnota 15 dekadicky if (1 != 0) if (i == 15) if (-12345 != 0) if (0 != 0) if ((i – 15) != 0) if ((i * 0) != 0) if ((i & 0 xf 0) != 0) // doporucuji rozepsat. . . int i = 0 x 0 f; if (1) if (i == 15) if (-12345) if (0) if (i – 15) if (i * 0) if (i & 0 xf 0). . . // // inicializovano vždy splneno vždy splněno vždy nesplneno matouci, plete se s &&

Zkrácené vyhodnocení složených podmínek • U složených podmínek se vyhodnocuje výraz zleva • Pokud

Zkrácené vyhodnocení složených podmínek • U složených podmínek se vyhodnocuje výraz zleva • Pokud je znám výsledek, zbývající část se nevyhodnocuje. . . int i = 0 x 0 f; if ((i != 0) && funkce_x()) { telo_podminky; }. . . • Funkce_x se zavolá pouze v případě, že i je nenulové (= splněna první část podmínky spojené AND operací) – • . . . int i = 0 x 0 f; if ((i != 0) || funkce_x()) { telo_podminky; }. . . • – funkce musí logicky vracet celočíselnou hodnotu telo_podminky se vykoná pouze tehdy, pokud je i nenulové „a zároveň“ hodnota vrácená z funkce_x je nenulová Funkce_x se zavolá pouze v případě, že i je nulové (= nesplněna první část podmínky spojené OR operací) • funkce musí logicky opět vracet celočíselnou hodnotu telo_podminky se vykoná pouze tehdy, pokud je i nenulové „nebo“ hodnota vrácená z funkce_x je nenulová

Formátování textového výstupu. . . int _tmain(int argc, _TCHAR* argv[]) { int i; for(i

Formátování textového výstupu. . . int _tmain(int argc, _TCHAR* argv[]) { int i; for(i = 0; i < 48; i++) printf("%d %3 d %03 d %x %02 x %X %02 Xn", i, i, i, i); // 7 x promenna i return 0; } • Opakování formátování v printf – – – formátovací povely začínají % %d – celočíselná hodnota %x – celočíselná hexadecimálně (také %X) číslo udává počet míst/cifer výpisu nula říká "doplň zleva nulama" – %s – výpis textu daný adresou v paměti – %c – výpis znaku dle celočíselné hodnoty (ASCII kódování) – %f – float hodnota (desetinné číslo) • • V printf může být proměnná jako parametr vícekrát Počet a typ formátovacích povelů a předaných parametrů se v C nekontroluje !!

Cykly – společné části for (inicializace; podminka; rizeni_behu) telo_cyklu; inicializace; while (podminka) { telo_cyklu;

Cykly – společné části for (inicializace; podminka; rizeni_behu) telo_cyklu; inicializace; while (podminka) { telo_cyklu; rizeni_behu; } inicializace; goto konec_cyklu; zacatek_cyklu: telo_cyklu; rizeni_behu; konec_cyklu: if (podminka) goto zacatek_cyklu; náhrada cyklu for - for se dá beze změny funkčnosti nahradit cyklem while nebo pouhým testováním podmínky a 2 skoky - uvedené zápisy jsou ekvivalentní a funkční důležité aspekty této koncepce - for cyklus nemusí mít ani inicializaci ani část rizeni_behu - telo_cyklu může být prázdný příkaz - nutná je jen podmínka - všechny cykly se formálně předkládají na podobný kód = zjednodušení překladače - uvedené kódy se dají přeložit !! - nekonečný cyklus může být: while (1) {. . . } nebo for (; 1; ) {. . . }

Cykly – funkce graficky for while do-while přeložený kód inicializace for, while do-while podmínka

Cykly – funkce graficky for while do-while přeložený kód inicializace for, while do-while podmínka ANO výkonné tělo cyklu řídící proměnná NE podmínka NE výkonné tělo cyklu ANO výkonné tělo cyklu řídící proměnná ANO podmínka NE ANO

Jednoduché cykly. . . int _tmain(int argc, _TCHAR* argv[]) { int i; i =

Jednoduché cykly. . . int _tmain(int argc, _TCHAR* argv[]) { int i; i = 0; while (i < 10) printf("%dn", i); return 0; } • while může nahradit for • "zapomenuté" i++ – pokud je využit kód pro for cyklus, může se zapomenout připočtení 1 řídící proměnné cyklu – vznikne nekonečný cyklus !! – přerušit konzolový program lze pomocí CTRL+C • Jednoduchá náprava. . . int i; i = 0; while (i < 10) printf("%dn", i++); . . . // inkrement lze provést přímo při volání printf

Inkrement "před" a "po". . . int i; i = 0; while (i <

Inkrement "před" a "po". . . int i; i = 0; while (i < 10) printf("%dn", i++); . . . • • • . . . int i; i = 0; while (i < 10) printf("%dn", ++i); . . . inkrement (i = i + 1) proveden jako součást jiné akce (printf) pro i++ vypíše se správně 0 – 9 při ++i vypíše 1 – 10 zápis říká, zda se akce děje "před použitím" proměnné nebo až "po použití": • i++ znamená "vezmi obsah i a pak udělej i = i + 1" • ++i znamená "udělej i = i + 1 a pak tu hodnotu použij" v řadě případů to nevadí, zde se však rozdíl projevil . . . { int i = 0; while (i < 10) { printf("%dn", i); i++; }. . . • pro přehlednost lepší použít blok • srovnání ++i a i++ v tomto případě • = stejný výsledek

Další typy cyklů a realizace skokem • . . . int i = 0;

Další typy cyklů a realizace skokem • . . . int i = 0; do { printf("%dn", i); } while (++i < 10); . . . loop: printf("%dn", i); i++; if (i < 10) goto loop; . . . • do-while cyklus vyhodnocuje podmínku na konci efekt ++i a i++ zde opět funguje dle očekávání – záleží, zda je napřed ++ nebo podmínka • extrémně nedoporučená konstrukce !!! • proč nepoužít • – goto v C sice existuje – je velmi málo případů, kdy je nutné – znepřehlední kód – porušuje hierarchii vnoření – nelze strukturovaně kontrolovat ani překladačem – existují vhodnější zápisy obdobného chování v cyklech • funguje ve for, while, do-while • break – vyskočí "za" cyklus • continue – znovu na začátek cyklu zbytečné použití v semestrálce = vracíme k přepracování

Větvení switch-case switch(promenna_celocislna) { case hodnota_1: case hodnota_2: kod_12; break; case hodnota_3: kod_3; //

Větvení switch-case switch(promenna_celocislna) { case hodnota_1: case hodnota_2: kod_12; break; case hodnota_3: kod_3; // zde není break case hodnota_4: kod_34; break; default: kod_default; } • Vícenásobné větvení – podle celočíselné hodnoty – int, char, long příp. unsigned varianty • Nelze zadat interval, nutný výčet hodnot • Větev default se vykoná při nesplnění uvedených hodnot – nemusí být nezbytně poslední, je to ale zvykem • Povinná je alespoň jedna (libovolná) větev (hodnota) • Příkaz break ukončuje větev – není povinný – pokud chybí, vykonává se další větev podle zápisu ve zdrojovém kódu – doporučeno okomentovat – častý zdroj chyb (zapomenutý break) • Typicky se překládá jako tabulka skoků

Bitové maskování 1010 1111 0000 0001 AND ---------0000 0001 1010 1111 0000 OR ---------1111

Bitové maskování 1010 1111 0000 0001 AND ---------0000 0001 1010 1111 0000 OR ---------1111 1010 1111 • Pro zjištění hodnoty konkrétního bitu čísla se používá maskování – bitový AND, v místě log. 1 masky jsou zachovány bity hodnoty • Podobně lze separovat část čísla vymaskováním „vícebitovou“ maskou • Vynulování bitu/bitů maskováním – zápisem nové hodnoty vzniklé operací AND s maskou (opět log. 1 zachovává) • Nastavení bitu/ů maskováním – použita operace OR, nastaví log. 1 jako v masce unsigned int x = 0 xaaff; if ((x & 0 x 01) != 0). . . if ((x & 0 x 01) == 0 x 01). . . if (x & 0 x 01). . . if ((x & 0 x 0 f) != 0). . . if ((x & 0 x 0 f) == 0 x 0 f). . . x x x = x & 0 xff 00; &= 0 xff 00; = x | 0 xf 000; |= 0 xf 000; = (x & 0 xff 00) // // // 1010 1111 test, zda je nejnizsi bit v log. 1 jiny zapis zkraceny zapis – mene prehledny je alespon jeden bit z dolnich nastaven na log. 1 ? jsou vsechny 4 spodni bity v log. 1 ? (0000 1111) // zachovano hornich 8 bitu, vynulovano spodnich 8 bitu (dava 0 xaa 00) // zkraceny zapis prirazeni hodnoty // nastaveni hornich 4 bitu na log. 1, ostatni bez zmeny (dava 0 xfaff) // zkracene prirazeni | 0 xf 000; // vynulovano spodnich 8 bitu a nastaveno horni 4 bity (0 xfa 00)

Znaménko u signed typů • Celočíselné typy jsou defaultně znaménkové • • Záporné číslo

Znaménko u signed typů • Celočíselné typy jsou defaultně znaménkové • • Záporné číslo má nastaven nejvyšší bit (MSB) na log. 1, zbytek uložen ve dvojkovém doplňku Matematicky platí pro dělení a násobení • Posuvy hodnoty pro celá čísla platí • • Pro posun se používá typicky kladné číslo („o kolik bitů“) Pokud se operace týká záporného čísla, měl by výsledek být opět záporný, což např. pro posun vpravo neplatí (shora se doplní log. 0 = vznikne „nezáporné“ číslo) • – – možno změnit ve vlastnostech překladu (nedoporučujeme) rozsah od – (2 n/2) do (2 n/2 – 1) pro n-bitové – – – kladné s kladným dá kladné se záporným dá záporné se záporný dá záporné – – dělení 2 odpovídá posun vpravo >> násobení 2 odpovídá posun vlevo << – překladač přidává „znaménkovou korekci“ tak, aby byl výsledek opět správně záporný POZOR – možný zdroj chyb – – • různé překladače např. vkládají jen u dělení/násobení a ne u posuvů, jiné ji použijí i pro posuvy chyba se projeví jen pro záporná čísla ŘEŠENÍ – pro bitové operace a pokud nás zajímají obecně bity v číslech je nutno použít UNSIGNED datové typ (= vždy kladné )

Problém s ne-inicializací. . . int i; // i = 0; !! úmyslně "zapomenuto"

Problém s ne-inicializací. . . int i; // i = 0; !! úmyslně "zapomenuto" while (i < 10) printf("%dn", i++); . . . • u while cyklu může dojít k zapomenutí inicializace řídící proměnné • v případě sestavení typu "debug" – dostáváme běhovou chybu • v sestavení "release" – chyba se nedetekuje – hodnota proměnné neznámá – pomocí krokování zjistíme • při překladu dostáváme Warning • !! nepřehlížet varování !!

3. přednáška • Reprezentace hodnot v paměti • Ukazatelová aritmetika • Přetypování ukazatelů

3. přednáška • Reprezentace hodnot v paměti • Ukazatelová aritmetika • Přetypování ukazatelů

Vícebajtové hodnoty v paměti • Jednobajtové (8 -bitové) hodnoty odpovídají jednomu bajtu v paměti

Vícebajtové hodnoty v paměti • Jednobajtové (8 -bitové) hodnoty odpovídají jednomu bajtu v paměti – v klasickém C je to typ char a unsigned char – pozor - na moderních OS jsou např. znaky reprezentovány v Unicode, takže „písmenko“ nemusí zabírat právě jeden bajt • různé překladače se s tímto rozdílem chápání znaku a typu char vyrovnávají specifickým způsobem • Vícebajtové hodnoty mohou mít 2 reprezentace – Little Endian – nejnižší bajt uložen na nižší adrese (= dříve v paměti) – Big Endian – nejvyšší bajt napřed • Příklad – 32 -bitová hodnota 0 x 12345678 Little endian 78 56 34 Big Endian 12 12 34 56 78

Problém s rozdílnou endianitou • Svět procesorových platforem se dělí podle endianity – x

Problém s rozdílnou endianitou • Svět procesorových platforem se dělí podle endianity – x 86, některé jednočipy – Little Endian – procesory Motorola (nyní Freescale), 8051 a další – Big Endian • Pokud vezmeme část paměti na jednom systému a bajt po bajtu ji zkopírujeme do druhého s odlišnou endianitou, reprezentuje to jinou hodnotu • „Naše“ bajty v paměti 0 x 78563412 odpovídají – LE – 0 x 12345678 – BE – 0 x 78563412 • Není mechanismus jak tomu zabránit, proto se vždy v binárních formátech souborů (např. grafických) uvádí i endianita u vícebajtových položek • Totéž u sériových protokolů, pokud nejsou čistě bajtově orientované – např. CRC 16 u Mod. BUS je 2 -bajtové • Obejití problému – přenos dat „textově“ – obě strany komunikace si data převedou do „své“ vnitřní reprezentace

Ukazatele • Ukazatel je speciální datový typ, který reprezentuje adresu • Interně se vždy

Ukazatele • Ukazatel je speciální datový typ, který reprezentuje adresu • Interně se vždy jedná o celočíselnou hodnotu, velikost záleží na paměťovém modelu a tím také na max. adresovatelné paměti – – Win 32 – 32 bitů Win 64 – 64 bitů většina 8 -bitových procesorů – 16 bitů C 51 (8 -bit 8051) – 3 bajty (24 bitů) • v jednom bajtu typ paměťového prostoru • 16 -bitová adresa • S ukazateli lze provádět celočíselné operace (sčítání, odečítání, porovnání, teoreticky i násobení a dělení)

Ukazatel „na …“ • Ukazatel (=adresa) je vždy svázán s určitým datovým typem a

Ukazatel „na …“ • Ukazatel (=adresa) je vždy svázán s určitým datovým typem a podle toho se provádí ukazatelová aritmetika • Typ ukazatele se zapisuje – typ * – např. char *, int * • Speciální případ je „beztypový ukazatel“ – void * – s ním nelze provádět ukazatelovou aritmetiku – slouží k předávání „obecné“ adresy

Ukazatele a speciální operátory • Ukazatele mají specifické unární operátory – stojí samostatně před

Ukazatele a speciální operátory • Ukazatele mají specifické unární operátory – stojí samostatně před výrazem (!!) • výrazem může být jak proměnná, tak nějaký výpočet, případně konstanta (pro dereferenci) • Operátor reference - & – poskytne adresu „objektu“ – zde nejspíš proměnné v paměti – výsledkem je typ „ukazatel na …“ podle typu referencované hodnoty • &promenna_char – vrací ukazatel typu char* • &promenna_int – vrací ukazatel typu int* – neplést s AND ! • Operátor dereference - * – poskytne „hodnotu na adrese“ – výsledkem je hodnota typu dle ukazatele • *char_ukazatel – vrací hodnotu typu char • *int_ukazatel – vrací hodnotu typu int – neplést s násobením !

Přetypování • V jazyce C je možné přetypovat hodnoty prakticky jakýchkoliv typů – formálně

Přetypování • V jazyce C je možné přetypovat hodnoty prakticky jakýchkoliv typů – formálně se nový typ uvede před výraz v závorkách – (int)char_prom dává hodnotu typu int, která odpovídá obsahu „charové“ proměnné • Implicitní přetypování = nemusí se uvádět, např. – char na int a na long, – int na long – float na double • Při nevhodném použití vede na záludné problémy • Při chybějícím přetypování může překladač hlásit varování – např. int na char může hlásit, že může dojít ke ztrátě hodnoty vlivem menšího rozsahu char oproti int – podobně double na float dává varování na ztrátu přesnosti – operace se ale provede, protože varování neblokuje překlad – při uvedení explicitního (=uvedeného) přetypování není varování

Operátor sizeof() • Operátor sizeof vrací velikost obsazené paměti konkrétním typem nebo proměnnou (v

Operátor sizeof() • Operátor sizeof vrací velikost obsazené paměti konkrétním typem nebo proměnnou (v bajtech) – sizeof(char) dává 1 – sizeof(char_prom) dává 1 – sizeof(int) dává na Win 32 hodnotu 4 stejně jako sizeof(int_promenna) – sizeof(float) dává 4 – sizeof(jakykoliv_ukazatel) dává 4 pro Win 32 • Operátor sizeof je vyhodnocován v době překladu, ve výsledném kódu se tedy objeví konstanta jako jeho výsledek

Ukazatelová aritmetika • Přičtení (příp. odečtení) hodnoty k ukazateli je překladačem vypočítáno podle typu

Ukazatelová aritmetika • Přičtení (příp. odečtení) hodnoty k ukazateli je překladačem vypočítáno podle typu ukazatele, přesněji podle velikosti typu, na který ukazuje – – – char_ukaz + 1 znamená adresa +1 int_ukaz + 1 znamená adresa + velikost int (4 nebo 2) long_ukaz + 1 znamená adresa + 4 long_ukaz + 3 znamená adresa + 12 long_ukaz – 2 znamená adresa - 8 ? ? 0 1 2 3 4 5 6 *cptr *(cptr+1) *lptr *(cptr+5) *(lptr+1) 7 8 9 *((long *)(cptr + 7)) 10 11 char *cptr; long *lptr; cptr = (char *) lptr; *(lptr+2)

Ukazatelová aritmetika II • Jednu adresu v paměti je možné vypočítat mnoha způsoby z

Ukazatelová aritmetika II • Jednu adresu v paměti je možné vypočítat mnoha způsoby z různých ukazatelů • Je důležité, nad jakým typem ukazatele se provádí výpočet – „krok“ výpočtu závisí na velikosti typu, na který ukazatel ukazuje – formálně zápis ukazatel_na_typ + X odpovídá výpočtu: (adresa_nesena_v_ukazateli + X * sizeof(typ)) – výše použité X je v signed rozsahu, tedy možno i záporné • Při přiřazení „adres“ mezi různými typy ukazatelů nutno provést přetypování, jinak většina překladačů C dává Warning, případně Error – int_ukaz = char_ukaz; (!!) - int_ukaz = (int *)char_ukaz; – char_ukaz = &long_prom; (!!) - char_ukaz = (char *)&long_prom; • Aritmetiku logicky nelze použít na void *

Praktické užití ukazatelů k pohledu na obsah paměti • Úkol – vypsat obsah paměti,

Praktické užití ukazatelů k pohledu na obsah paměti • Úkol – vypsat obsah paměti, kterou obsahuje long proměnná #include "stdafx. h" int _tmain(int argc, _TCHAR* argv[]) { long a = 0 x 12345678; int i; // pomocna promenna char *cptr; // promenna typu ukazatel cptr = (char *)&a; // reference – ziskani adresy objektu for (i = 0; i < sizeof(int); i++) // cyklus podle obsazene pameti printf("%x, ", *(cptr + i)); // vypis hodnoty na adrese dane ukazatelem printf("n"); return 0; } // odradkovani po vypisu

Pole jako jiný zápis ukazatelové aritmetiky • V C de-facto neexistují pole • Zápis

Pole jako jiný zápis ukazatelové aritmetiky • V C de-facto neexistují pole • Zápis pomocí [] se překládá jako vyjádření ukazatelové aritmetiky – např. A[2] odpovídá *(A + 2) • logicky A musí být typu ukazatel – někdy též „báze“ • hodnota v závorkách musí být celočíselná – někdy též „offset“ – možné důsledky • A[0] odpovídá *A • je možné napsat A[-10] – formálně v pořádku – sáhne se do paměti „před“ adresu v ukazateli A • je možné prohodit bázi a offset – 2[A] nejde kvůli chybějícímu přetypování – lze ((int *)2)[(int)A] s tím, že A bylo int* a int a ukazatel jsou stejně velké (na Win 32 tedy 4 bajty) • Volba zápisu je na zvyklostech programátoru nebo typu algoritmu

Použití zápisu jako pole • Náhrada výpočtu ukazatelů „polem“. . . for (i =

Použití zápisu jako pole • Náhrada výpočtu ukazatelů „polem“. . . for (i = 0; i < sizeof(int); i++) printf("%x, ", cptr[i]); // vypis hodnoty na adrese indexem v poli. . . • Obsah cptr se během programu nemění, proto není nutno jej ukládat proměnné a rovnou použít adresu long proměnné a printf("%x, ", *(((char *)&a) + i); • Případně totéž pomocí „pole“ printf("%x, ", ((char *)&a)[i]);

Float – reprezentace v paměti • Float podle IEEE-754 – 32 bitová hodnota –

Float – reprezentace v paměti • Float podle IEEE-754 – 32 bitová hodnota – 1 bit znaménko (MSB) – 8 bitů exponent – posunutý o 127 – 23 bitů mantisa – uloženo jako desetinná část, neukazuje se 1 ("nepřiznaná") • Význam zobrazených dat pro příklad -11 – – bajty v paměti - 00, 30, C 1 pohled jako unsigned long - c 1300000 binárně - 11000001 001100000000 pracujeme v Little Endian prostředí byte 0 1 1 0 0 0 byte 1 0 0 1 1 0 0 0 . . . 0 0

Float – rekontrukce hodnoty byte 0 1 1 0 0 0 byte 1 0

Float – rekontrukce hodnoty byte 0 1 1 0 0 0 byte 1 0 0 1 1 0 0 0 . . . 0 0 • Výpočet z dat – v paměti máme hodnotu c 1300000 – záporné číslo (znaménko == 1) – exponent je 1000 0010 • hodnota 130, při posunu o 127 je 130 – 127 = 3 • tedy základ čísla 2 exp, tedy 2(130 -127) = 23 = 8 – mantisa reálně 1 ≤ M < 2, udána bez 1 (ta se automaticky považuje za skrytou) – každý bit mantisy od MSB odpovídá 2 -1, 2 -2, . . . 2 -23 – mantisa je 011 0000 0000 • zde jsou log 1 na pozici pro: 2 -2 + 2 -3 • celkem: 0, 25 + 0, 125 = 0, 375 bez neuváděné 1, 0 • Výsledek – 8 * (1, 0 + 0, 375) = 8 * 1, 375 = 11 – podle znaménka to bude záporné, tedy -11, 0

Reprezentace desetinných čísel • • • § Ve většině případů jsou uložena ve formátu

Reprezentace desetinných čísel • • • § Ve většině případů jsou uložena ve formátu tzv. plovoucí řádové čárky (floating point) Jedná se o zobrazení s přesností omezenou na určitý počet platných míst, kde počet platných míst a rozsah je omezen velikostí paměti, která je k dispozici pro zobrazení desetinných čísel a kolik paměti je k dispozici pro jednotlivé složky desetinných čísel – znaménko Z, exponent E a mantisu M byte bit Z M E P float 4 32 1 23 8 127 double 8 64 1 52 11 1023 long double 10 80 cislo = Z * (1 + m/2 M) * 2(e-P) Kde: § § § Z - znaménko - +1 nebo -1 m - mantisa, celé číslo v rozsahu 0. . . 2 M-1 M - velikost mantisy v bitech (23 nebo 52) e - exponent, celé číslo v rozsahu 0. . . 2 E-1 E - velikost exponentu v bitech (8 nebo 11) P - posunutí exponentu (127 nebo 1023) nepoužívat, není standard • příklady: § § 1. 0 2. 0 3. 0 0. 1 . . . Z=+1, m=0, e=127 m=0, e=128 m=4194304, e=128 m=5033165, e=123

5. přednáška Struktury Hromada (heap)

5. přednáška Struktury Hromada (heap)

Struktury • V jazyce C je možno vytvářet nové – složené – datové typy

Struktury • V jazyce C je možno vytvářet nové – složené – datové typy – jmeno_struktury není povinné • • pokud rovnou vytvoříme jednu proměnnou nemůžeme však použít jako datový typ – v C se musí se jmeno_struktury používat i struct • • lze obejít pomocí typedef Přístup k prvkům struktury pomocí operátoru „tečka“: proměnná_struct. atribut • Máme-li ukazatel na strukturu, přistupuje se k prvkům pomocí operátoru „šipka“: ukazatel_struct->atribut – Visual. Studio „napovídá“, pokud máme výše korektní definici struct • Prvky struktury možno inicializovat pomocí zápisu { } – v příkladu podobně inicializováno i pole struct jmeno_struktury { slozky. . . } jmeno_promenne; struct data. Item { int id; char attr[2]; int value; }; . . . data. Item d = {0, {'a', 'b'}, 0}; *pd; printf("%d %c %c %dn", d. id, d. attr[0], d. attr[1], d. value); pd = &d; pd->id = 10; pd->attr[0] = 'x'; *(pd->attr + 1) = 's'; printf("%d %c %c %dn", d. id, d. attr[0], d. attr[1], d. value);

Typedef • Typedef obecně dokáže vytvářet nové datové typy • Často se používá u

Typedef • Typedef obecně dokáže vytvářet nové datové typy • Často se používá u struktur a unií – není pak nutno uvádět při použití klíčové slovo struct – není povinné uvádět jmeno_struktury, pokud se na ni nepotřebujeme uvnitř odkázat • potom je nutné uvnitř použít klíčové slovo struct • používá se často u spojových seznamů – odkaz na následující prvek • Možno vytvořit i přejmenované nebo odvozené datové typy – elegantní řešení pro usnadnění „psaní kódu“ typedef struct jmeno_struktury { prvky. . . } jmeno_noveho_typu; typedef struct data. Item_struct { prvky. . . ; struct data. Item_struct *next; } data. Item; typedef word unsigned int; typedef byte unsigned char; . . . byte b = 0 xff; word w = 0;

Zarovnávání prvků struktur #include "stdafx. h" typedef struct { int id; char attr[2]; int

Zarovnávání prvků struktur #include "stdafx. h" typedef struct { int id; char attr[2]; int value; } data. Item; int _tmain(int argc, _TCHAR* argv[]) { data. Item d = {0 x 12345678, {'a', 'b'}, -1}; unsigned char *uptr; int i; printf("%d %d %d * %X %c %c %dn", sizeof(d), sizeof(d. id), sizeof(d. attr[0]), d. id, d. attr[0], d. attr[1], d. value); uptr = (unsigned char *)&d; for (i = 0; i < sizeof(d); i++) printf("%02 X, ", uptr[i]); // stejne pri pouziti sizeof(data. Item) puts(""); return 0; } sizeof - velikost dat v paměti - typicky datového typu nebo proměnné - vyčíslována v době překladu - aplikovatelné i na struktury a jejich prvky výpis obsahu struktury - použit stejný "trik" jako při výpisu proměnné long - ukazatel na bajty (char) - unsigned kvůli znaménkovým korekcím a především kvůli výpisu hodnot 128– 255 (0 x 80– 0 x. FF) – jinak vypisováno jako záporné hodnoty zarovnávání na 32 bitů - prvky struktury se zarovnávají - přizpůsobení cílové platformě – zde Win 32 - celková velikost != součet velikosti prvků

Nastavení zarovnání prvků struktur zarovnávání struktur možno překladači nastavit - překladače C (i C++)

Nastavení zarovnání prvků struktur zarovnávání struktur možno překladači nastavit - překladače C (i C++) umožňují změnit Struct Alignment - ve Visual Studio – menu Project/Properties –> Configuration –> C/C++ –> Code Generation - !!! POZOR ! nezarovnáním na celý násobek datového slova procesoru se snižuje výkon, některé RISC systémy dokonce vyvolávají chyby přístupu do "nezarovnané" paměti !!! - další info - http: //en. wikipedia. org/wiki/Data_structure_alignment výpis paměti v debuggeru - breakpoint na libovolné místo - okno „Memory“ - do adresy zadat &d – vyčíslí skutečnou adresu automaticky - prohlédnout paměť

Bitová pole • Prvky struktury mohou být celočíselné hodnoty s omezeným rozsahem – dáno

Bitová pole • Prvky struktury mohou být celočíselné hodnoty s omezeným rozsahem – dáno počtem bitů – překladač by pak měl tato data uložit co nejvýhodněji do paměti, aby využil prostor – počet bitů uveden za : – typicky jsou to unsigned hodnoty – možná hodnota dána rozsahem bitů #include "stdafx. h" typedef struct { unsigned int hodnota 1: 5; unsigned int hodnota 2: 8; unsigned int hodnota 3: 3; } packet. Bits; int _tmain(int argc, _TCHAR* argv[]) { int i; packet. Bits pb = { 0, 0, 0 }; for ( i = 0; i < 256; i++) { pb. hodnota 1 = i; pb. hodnota 2 = i; pb. hodnota 3 = i; • Příklad plní jednotlivé složky hodnotou 0 -255 – do 8 -bitové se vejde „akorát“ – do menších jen příslušná část printf("I=%3 d PB: %3 d %3 dn", i, pb. hodnota 1, pb. hodnota 2, pb. hodnota 3); } return 0; }

Union • Zobecnění myšlenky „různých pohledů na tutéž paměť“ – formálně se podobají strukturám

Union • Zobecnění myšlenky „různých pohledů na tutéž paměť“ – formálně se podobají strukturám • použití typedef • přístup k atributům – jednotlivé složky nejsou v paměti „za sebou“, ale zabírají společnou paměť a je možno je použít k přístupu všechny – pokud nejsou složky stejně velké, zabranou paměť určuje „největší“ #include "stdafx. h" typedef union { unsigned long hodnota; unsigned char pole[4]; } ulong_bajty; int _tmain(int argc, _TCHAR* argv[]) { int i; ulong_bajty uba; uba. hodnota = 0 x 12345678; • Možno např. použít v algoritmu „výpisu paměti“ proměnné for(i = 0; i < 4; i++) printf("%02 X, ", uba. pole[i]); – funguje stejně, bez ukazatelů putchar('n'); return 0; } 0 x 78 0 x 56 0 x 34 0 x 12 union unsigned long 0 x 12345678 unsigned char[4] 0 x 78, 0 x 56, 0 x 34, 0 x 12

Union a bitová pole • Elegantní přístup k bitovým složkám i celé hodnotě najednou

Union a bitová pole • Elegantní přístup k bitovým složkám i celé hodnotě najednou • Např. výpis složek float – pozor na pořadí – zde funkční pro Little. Endian – stejný výsledek jako výpisy ve 3. př. a cv. • Změnou exponentu se mění skutečná hodnota reprezentující „float“ pohled #include "stdafx. h" typedef union { unsigned int hodnota; float des_hodnota; struct { unsigned int mantisa: 23; unsigned int exponent: 8; unsigned int znamenko: 1; }; unsigned char pole[4]; } uint_float; int _tmain(int argc, _TCHAR* argv[]) { int i; uint_float uf; uf. des_hodnota = -11. 0; printf("%f %08 X %d %x %xn", uf. des_hodnota, uf. znamenko, uf. exponent, uf. mantisa); for( i = 0; i < 256; i++) { uf. exponent = i; printf(" %3 d %08 X %. 12 fn", i, uf. hodnota, uf. des_hodnota); } return 0; }

Umístění dat/proměnných programu • Oblast pro data známá během překladu – zásobník (stack) –

Umístění dat/proměnných programu • Oblast pro data známá během překladu – zásobník (stack) – velikost obsazené paměti určí překladač – zároveň slouží k předávání parametrů do funkcí a ukládání návratové hodnoty – všechny lokální i globální proměnné • Oblast pro data, jejichž velikost se určuje za běhu programu – halda (heap, hromada) – část paměti přidělená procesu systémem – pomocí „správce hromady“ si lze vyžádat „blok paměti“ nebo jej vrátit, pokud již není potřeba • bloky jsou identifikovány svojí adresou = ukazatel na počátek • správce hromady „si drží“ informaci o velikosti bloku, aby jej pak mohl uvolnit – v principu spojový seznam „hlaviček“ bloků

Hromada - heap • malloc – alokování bloku paměti – void * malloc(size_t size)

Hromada - heap • malloc – alokování bloku paměti – void * malloc(size_t size) – návratová hodnota se MUSÍ přetypovat, jinak s tou adresou (ukazatelem) nelze dělat ukazatelovou aritmetiku • free – uvolnění bloku – void free(void * memblock) – adresu musí být začátek bloku dříve „malloc-ovaného“ • calloc – vnitřně volá malloc – void * calloc(size_t num, size_t size) – alokuje num*size bajtů • POZOR – časté chyby: – tradičně se nekontroluje přístup jen do alokovaného bloku • možno „chybně“ sáhnout kamkoliv do haldy resp. do paměti – – ztracený ukazatel začátku bloku – nelze pak uvolnit a hromada „kyne“ PRAVIDLO – každý malloc má svůj free alokování malých bloků má relativně velkou režii (každý cca 16 B) může dojít k fragmentaci • v hromadě je sice x bajtů volného místa, ale není „v kuse“ a nelze tedy naalokovat větší blok než největší „volné místo“ • typicky není informace o obsazení hromady, malloc buď dá platnou adresu bloku (=OK) nebo NULL (=není tak velká souvislá oblast) • není k dispozici žádný „defragmentátor“, jedině uvolnit nepotřebné bloky paměti v aplikaci

Základní příklad práce s haldou #include "stdafx. h" //#include <malloc. h> #define BLOK_SIZE 26

Základní příklad práce s haldou #include "stdafx. h" //#include <malloc. h> #define BLOK_SIZE 26 int _tmain(int argc, _TCHAR* argv[]) { int i; char *cptr; if (NULL == (cptr = (char *)malloc(BLOK_SIZE))) return -1; // konec pri chybe for (i = 0; i < BLOK_SIZE; i++) cptr[i] = 'A' + i; // plneni bloku for (i = 0; i < BLOK_SIZE; i++) putchar(cptr[i]); // vycteni bloku putchar('n'); free(cptr); return 0; } // NEZAPOMENOUT !!! dvojice malloc/free - párové funkce pro práci s paměťovým prostorem "hromada" (HEAP) - součást std. knihovny, někdy nutno připojit hlavičku malloc. h - ZÁSADA – každý malloc má svůj free malloc - vrací void *, nutno vždy přetypovat - při selhání vrací NULL (= málo místa) - vždy nutno testovat !!

Možné chyby práce s haldou v Debug sestavení. . . for (i = 0;

Možné chyby práce s haldou v Debug sestavení. . . for (i = 0; i <= BLOK_SIZE; i++) cptr[i] = 'A' + i; . . . chyba zápisu mimo alokovaný prostor - vyvolá běhovou chybu při free - v Debug sestavení je „okolo“ alokované paměti výplňový vzor - interně se volá _malloc_dbg - integrita vzoru se testuje při free . . . for (i = 0; i <= BLOK_SIZE; i++) putchar(cptr[i]); . . . chyba čtení mimo alokovaný prostor - běžně nevyvolá běhovou chybu - v Debug sestavení vyčte bajt z výplňového vzoru - pokud by se přistoupilo mimo paměť procesu, možná chyba ochrany paměti . . . free(cptr + 1); . . . chyba uvolnění paměti z jiné adresy - běhová chyba v Debug sestavení - testuje se, zda ukazatel je platný v haldě (heap) procesu a příp. se vyvolá chyba http: //msdn. microsoft. com/en-us/library/974 tc 9 t 1(v=vs. 90). aspx

Pomocné funkce pro práci s pamětí • • Po alokování paměti z hromady nebo

Pomocné funkce pro práci s pamětí • • Po alokování paměti z hromady nebo vytvoření proměnné na zásobníku je obsahem v podstatě to, co bylo v té fyzické oblasti paměti před tím => nutno inicializovat Pro blok paměti možno použít blokové paměťové funkce z memory. h #include "stdafx. h" #include <stdlib. h> #include <memory. h> typedef struct { unsigned int x; unsigned char a[2]; } data. Item; int _tmain(int argc, _TCHAR* argv[]) { data. Item *pd; void * memset(void *dest, int c, size_t count); if (NULL == (pd = (data. Item *) malloc(sizeof(data. Item)))) return -1; • nastavení bajtů bloku paměti na hodnotu c void *memcpy(void *dest, const void *src, size_t count); printf("%08 X %02 Xn", pd->x, pd->a[0], pd->a[1]); • zkopíruje blok paměti, žádná kontrola cílového místa int memcmp(const void *buf 1, const void *buf 2, size_t count); memset(pd, 0 x 80, sizeof(data. Item)); • porovnání obsahu 2 bloků paměti printf("%08 X %02 Xn", pd->x, pd->a[0], pd->a[1]); free(pd); return 0; }

6. přednáška Dynamické algoritmy Spojový seznam Aplikace spojového seznamu

6. přednáška Dynamické algoritmy Spojový seznam Aplikace spojového seznamu

Spojový seznam *_first. Item 1 2 3 *next. Item NULL • Typicky využívá struktury

Spojový seznam *_first. Item 1 2 3 *next. Item NULL • Typicky využívá struktury a hromadu • Ve struktuře nesena užitečná data a odkaz (=ukazatel) na další prvek v seznamu • Položky seznamu se dynamicky vytvářejí na hromadě typedef struct data. Item_struct { int value; char popis[20]; data. Item_struct *p. Next. Item; } data. Item; typedef struct data. Item_struct_dbl { int value; char popis[20]; data. Item_struct_dbl *p. Next. Item; data. Item_struct_dbl *p. Prev. Item; } data. Item. Double; • Užitečná data – int hodnota – char pole pro text • Ukazatel na další prvek – nutno předem uvést název struktury, aby mohl pak být uvnitř ukazatel – podle C++ nemusí být uvedeno struct při použití (zde v ukazateli) – jeden ukazatel stačí pro realizaci jednosměrně zřetězeného seznamu – dva ukazatele pro obousměrně zřetězený = máme k dispozici i odkaz na předchůdce

Jednosměrný spojový seznam data. Item *_p. First. Item = NULL; data. Item *_Search. Last(void)

Jednosměrný spojový seznam data. Item *_p. First. Item = NULL; data. Item *_Search. Last(void) { data. Item *p. Temp; if (_p. First. Item == NULL) return NULL; for (p. Temp = _p. First. Item; p. Temp->p. Next. Item != NULL; p. Temp = p. Temp->p. Next. Item) ; return p. Temp; } • Vnitřní proměnné a funkce algoritmu pojmenovány _xxx • Ukazatel na první prvek _p. First. Item • Při hledání posledního prvku se prochází seznamem přes následníky až k takovému, který má adresu následníka NULL – vizuálně to nahrazuje zapouzdření z objektových jazyků – pro prázdný seznam je NULL – pro prázdný seznam vrací NULL

Jednosměrný spojový seznam - 2 bool _Show. Content(void) { data. Item *p. Temp; printf("n.

Jednosměrný spojový seznam - 2 bool _Show. Content(void) { data. Item *p. Temp; printf("n. FIFO Content: (first=%p)n", _p. First. Item); if (_p. First. Item == NULL) { puts("Emptyn"); return false; } for (p. Temp = _p. First. Item; p. Temp != NULL; p. Temp = p. Temp->p. Next. Item) printf("%p %d %sn", p. Temp->value, p. Temp->popis); puts(""); return true; } • • • U většiny algoritmů nepotřebujeme znát obsah prvků, ale najít první, poslední či konkrétní Uvedená funkce pomáhá vypsat seznam pro lepší pochopení funkce – vypisuje se adresa prvku a obě složky „užitečných“ dat – – cyklus proběhne i pro poslední prvek a konec je „za“ seznamem je to mírně odlišné chování oproti hledání posledního, kde se skončilo posledním Iterace ve for probíhá, dokud pomocný ukazatel p. Temp ukazuje na platný prvek seznamu

FIFO (= fronta) - princip First In First Out - v podstatě se jedná

FIFO (= fronta) - princip First In First Out - v podstatě se jedná o frontu datových prvků, která má „počátek“ *_first. Item - jednotlivé prvky obsahují ukazatel na následující prvek *next. Item - poslední prvek má *next. Item nastaven na hodnotu NULL - jako následující přidání znamená vytvořit položku (zřejmě malloc) a dát ji na konec „řetězu“ - odebrání vyjme položku ze začátku a posune začátek na následující položku, odebranou uvolní pomocí free z hromady Počáteční stav – prázdná fronta: *_first. Item NULL 1 *_first. Item První položka: Přidány další 2: Odebrání: Odebrán další prvek: *_first. Item *next. Item 1 2 3 *next. Item 2 3 *next. Item 12 *next. Item NULL *_first. Item NULL 3 *_first. Item *next. Item NULL

FIFO – funkce přidání na konec int FIFO_Add(int value, char *txt) { static int

FIFO – funkce přidání na konec int FIFO_Add(int value, char *txt) { static int i. Calls = 0; if (NULL == (p. New = (data. Item *)malloc(sizeof(data. Item)))) return -1; ukazatele na strukturu - „nový“ pro nově vytvořenou položku, která se má zařadit na konec fronty - „pomocný“ pro uschování „posledního prvku“ p. New->p. Next. Item = NULL; p. New->value = value; if (strlen(txt) >= 20) p. New->popis[0] = 0; else strcpy(p. New->popis, txt); nová položka - testuje se úspěšnost funkce malloc - next. Item vynulováno = bude poslední - přepsány hodnoty z parametrů funkce - kontrolována velikost textu před strcpy if (NULL == (p. Temp = _Search. Last())) _p. First. Item = p. New; else p. Temp->p. Next. Item = p. New; zařazení na konec - pokud není nalezen poslední prvek, je nový prohlášen „prvním“ - jinak je nový prohlášen následníkem „doteď-posledního“ a nový se tedy stává posledním data. Item *p. New, *p. Temp; return ++i. Calls; } počítadlo úspěšných zavolání funkce - příklad využití statické proměnné

FIFO – funkce odebrání z čela bool FIFO_Remove(int *p. Value, char *txt) { data.

FIFO – funkce odebrání z čela bool FIFO_Remove(int *p. Value, char *txt) { data. Item *p. Temp; if (_p. First. Item == NULL) return false; p. Temp = _p. First. Item; *p. Value = p. Temp->value; strcpy(txt, p. Temp->popis); _p. First. Item = _p. First. Item->p. Next. Item; free(p. Temp); return true; } návratová hodnota - pro prázdnou frontu vrací false - pokud se podaří správně odebrat, vrací true a naplní data (odkázaná ukazateli) posun prvního prvku - odebíráme z čela fronty - ukazatel na první prvek se posunuje na následující - pokud je pouze 1 prvek, následníkem je NULL, to se přenese do ukazatele na prvního = fronta prázdná dočasný ukazatel - napřed se musí přesunout první na dalšího - pak teprve je možno uvolnit z hromady - nutno si tu adresu „někde“ uložit nezapomenout na free - pořád platí „každý malloc má svůj free“

LIFO (= zásobník) – princip Last In First Out - v podstatě se jedná

LIFO (= zásobník) – princip Last In First Out - v podstatě se jedná o zásobník datových prvků, který má „počátek“ *_first. Item - jednotlivé prvky obsahují ukazatel na následující prvek *next. Item - poslední prvek má *next. Item nastaven na hodnotu NULL - jako následující přidání znamená vytvořit položku (pomocí malloc) a dát ji na konec „řetězu“ - odebrání odebere položku z konce, takže zatím „předposlední“ se stává nově „posledním“ Počáteční stav – prázdný zásobník: *_first. Item NULL 1 První položka: Přidány další 2: Odebrání: *_first. Item *next. Item NULL 1 2 3 *next. Item 1 2 *next. Item 1 Odebrán další prvek: *_first. Item *next. Item NULL 3 NULL *next. Item 12 *next. Item

bool LIFO_Remove(int *p. Value, char *txt) { data. Item *p. Temp; if (_p. First.

bool LIFO_Remove(int *p. Value, char *txt) { data. Item *p. Temp; if (_p. First. Item == NULL) return false; for (p. Temp = _p. First. Item; p. Temp->p. Next. Item != NULL; p. Temp = p. Temp->p. Next. Item) ; *p. Value = p. Temp->value; strcpy(txt, p. Temp->popis); free(p. Temp); if (p. Temp == _p. First. Item) _p. First. Item = NULL; else { data. Item *p. Hledat; for (p. Hledat = _p. First. Item; p. Hledat->p. Next. Item != p. Temp; p. Hledat = p. Hledat->p. Next. Item) ; p. Hledat->p. Next. Item = NULL; } return true; } LIFO rozdíl proti FIFO prázdný zásobník - vrať false = chyba najít posledního - iteruj prvky od prvního po posledního - má next. Item s hodnotou NULL užitečná data - vezmi data z posledního nezapomenout na free - pořád platí „každý malloc má svůj free“ jen jeden prvek v zásobníku ? - poslední == první - nastav „prázdný zásobník“ předposlední se stává posledním - iterovat seznam do prvku, kde next. Item je adresou „posledního“ (uloženo v p. Temp) - nastavit mu next. Item na NULL - nevadí, že již byl poslední uvolněn z hromady, jde nám jen o tu adresu v řetězu prvků seznamu - fígl s lokální proměnnou v else bloku !

Sorted-List – princip Samotřiditelný seznam - jedná se o speciální variantu jednosměrně zřetězeného seznamu

Sorted-List – princip Samotřiditelný seznam - jedná se o speciální variantu jednosměrně zřetězeného seznamu - nově vkládaná položka se automaticky umístí tak, aby seznam zůstával setříděný - obdobně při odebírání se naváže seznam tak, aby byl stále setříděný - první položka je *_first. Item, alokace se provádí tradičně malloc, dealokace pomocí free Počáteční stav – prázdný seznam: *_first. Item NULL 45 První položka: Přidána další: – na počátek Přidána další: - doprostřed *_first. Item *next. Item NULL 45 90 *next. Item 10 45 90 *next. Item NULL 10 NULL *next. Item 60 NULL *next. Item

Sorted-List – princip 2 45 10 Odebrání z prostředka: Odebrání z konce: • •

Sorted-List – princip 2 45 10 Odebrání z prostředka: Odebrání z konce: • • • *_first. Item *next. Item 10 60 *next. Item 60 90 *next. Item 90 NULL *next. Item Pro realizaci lze použít stejnou strukturu jako pro předchozí implementace spojového seznamu – klíčem pro třídění bude položka value Funkce _Show. Content a adresa začátku v ukazateli _p. First. Item jsou také totožné Je tedy nutno napsat funkce pro přidání a odebrání – bool SLIST_Get(int value, char *txt) • očekává hodnotu klíče, pokud najde, zkopíruje obsah „textových dat“ na adresu dle druhé parametru • vrací příznak úspěchu (true = nalezeno) – bool SLIST_Add(int value, char *txt) • vytvoří a zařadí do seznamu nový prvek podle klíče a naplní textovým obsahem • vrací příznak úspěchu (false = nelze alokovat pomocí malloc) NULL

bool SLIST_Get(int value, char *txt) { data. Item *p. Temp, *p. Temp 1; if

bool SLIST_Get(int value, char *txt) { data. Item *p. Temp, *p. Temp 1; if (_p. First. Item == NULL) // prazdny ? return false; // priznak chyby Sorted-List – GET if (_p. First. Item->value == value) // je to ten první ? { strcpy(txt, _p. First. Item->popis); // uzitecna data p. Temp = _p. First. Item->p. Next. Item; // adresa druheho free(_p. First. Item); _p. First. Item = p. Temp; return true; // prvniho uvolnit // „novy“ první prvek // priznak OK } for (p. Temp 1 = _p. First. Item, p. Temp = _p. First. Item->p. Next. Item; p. Temp != NULL; p. Temp 1 = p. Temp, p. Temp = p. Temp->p. Next. Item) { // všechny prvky od druheho dale if (p. Temp->value == value) // je to on ? { strcpy(txt, p. Temp->popis); // vezmi data p. Temp 1 ->p. Next. Item = p. Temp->p. Next. Item; // „premostit“ free(p. Temp); // uvolni nalezeneho return true; // priznak OK } } return false; // nic uspesneho = chyba } okrajové podmínky - 1. prázdný seznam - 2. první prvek --- změnit _p. First. Item iterační cyklus - počínaje 2. prvkem - krokuje se přes dva sousedící prvky - řídící je p. Temp shoda klíče - přes ukazatele se přemostí nalezený - funguje i pro poslední prvek

Sorted-List – ADD – díl 1 bool SLIST_Add(int value, char *txt) { data. Item

Sorted-List – ADD – díl 1 bool SLIST_Add(int value, char *txt) { data. Item *p. New, *p. Temp 1; if (NULL == (p. New = (data. Item *)malloc(sizeof(data. Item)))) return false; p. New->p. Next. Item = NULL; // inicializace p. New->value = value; // klic if (strlen(txt) >= 20) // textova data p. New->popis[0] = 0; else strcpy(p. New->popis, txt); if (_p. First. Item == NULL) { _p. First. Item = p. New; return true; }. . . // prazdny seznam ? // zacina tim novym nový prvek - kontrola provedení malloc - naplnění klíče a bezpečně textu - předběžně „poslední“ okrajové podmínky - 1. prázdný seznam --- stačí jen nastavit _p. First. Item - 2. vložit před první prvek --- zařadit do seznamu na začátek - 3. je zatím jen jeden prvek --- nový se stane druhým i posledním iterační cyklus - počínaje 2. prvkem - krokuje se přes dva sousedící prvky - řídící je p. Temp nový poslední prvek - pokud se nenalezla dvojice prvků, mezi které by se nový vložil

Sorted-List – ADD 2 . . . if (_p. First. Item->value > value) //

Sorted-List – ADD 2 . . . if (_p. First. Item->value > value) // prvni vetsi ? { // musi se tedy novy stat prvnim p. New->p. Next. Item = _p. First. Item; _p. First. Item = p. New; return true; } if (_p. First. Item->p. Next. Item == NULL) // neni druhy prvek ? { // novy zde nebyl vetsi _p. First. Item->p. Next. Item = p. New; // stane se druhym return true; } for(p. Temp 1 = _p. First. Item, p. Temp = _p. First. Item->p. Next. Item; p. Temp != NULL; // ten aktualni p. Temp 1 = p. Temp, p. Temp = p. Temp->p. Next. Item) { // v temp 1 se drzi "predchazejici" if (p. Temp->value > value) // novy mensi nez aktualni ? { // takze „vlozit" mezi p. Temp 1 ->p. Next. Item = p. New; // predchazejici na novy p. New->p. Next. Item = p. Temp; // novy na aktualni return true; } } p. Temp 1 ->p. Next. Item = p. New; return true; } // pridat na konec seznamu // konec SLIST_ADD

Obousměrně zřetězený seznam – příklady operací typedef struct_item_dbl { int value; // char text[20];

Obousměrně zřetězený seznam – příklady operací typedef struct_item_dbl { int value; // char text[20]; // struct_item_dbl *p. Next; // struct_item_dbl *p. Prev; // } item. Dbl; uzitecna ukazatel data nebo take klic k vyhledavani polozky data na nasledujici prvek nebo NULL pro posledni na prechozi prvek nebo NULL pro prvni item. Dbl *_p. First = NULL; item. Dbl *_p. Last = NULL; // adresa prvniho prvku // adresa posledniho prvku (mozno doiterovat podle prvniho) void _vypis(bool dopredne) { item. Dbl *p. Temp; // funkce vypise obsah od zacatku nebo od konce seznamu if (_p. First == NULL) { puts("Prazdny seznam"); return; } // docasny/pomocny ukazatel // prazdny ? (mozno i podle _p. Last == NULL) if (dopredne) // vetveni podle smeru vypisu { for(p. Temp = _p. First; p. Temp != NULL; p. Temp = p. Temp->p. Next) printf("%p %d %sn", p. Temp->value, p. Temp->text); } else { for(p. Temp = _p. Last; p. Temp != NULL; p. Temp = p. Temp->p. Prev) printf("%p %d %sn", p. Temp->value, p. Temp->text); } } // prochazim podle nasledniku // prochazim podle prechudcu

bool Add. To(int val, char *tx, bool na. Konec) { item. Dbl *p. New;

bool Add. To(int val, char *tx, bool na. Konec) { item. Dbl *p. New; // vytvor a pridej novy prvek na konec/zacatek if (NULL == (p. New = (item. Dbl *)malloc(sizeof(item. Dbl)))) // alokace return false; // priznak chyby memset(p. New, 0 x 00, sizeof(item. Dbl)); // vynulovanim obsahu se vynuluji i ukazatele p. New->value = val; // uzitecna data strncpy(p. New->text, tx, 20); // kopiruje jen N znaku retezce p. New->text[19] = 0 x 00; // POZOR, pokud strncpy orizl, neudela ukoncovaci nulu, proto rucne if (_p. First == NULL) // prazdny seznam ? { _p. First = p. New; // tak ten novy je prvni _p. Last = p. New; // a zaroven i posledni } else { if (na. Konec) // kam pridat ? { p. New->p. Prev = _p. Last; // stavajici posledni bude prechudcem noveho _p. Last->p. Next = p. New; // novy bude naslednikem doted posledniho _p. Last = p. New; // a novy se stava poslednim “oficialne” } else // pridat na zacatek { p. New->p. Next = _p. First; // naslednikem noveho bude stavajici prvni _p. First->p. Prev = p. New; // novy bude prechudcem doted prvniho _p. First = p. New; // a novy se stava prvnim } } return true; } // provedeno OK