{{indexmenu_n>2}} ====== Přednášky ====== Podklady přednášek pro aktuální akademický rok 2021/2022. Podklady se skládají z promítaných slidů, které jsou také k dispozici ve zkrácených verzích šetrnějších k případnému tisku, bez přechodů mezi snímky a ve formátu čtyř a devíti snímků na stránku. **Podklady tvoří podpůrný materiál a jejich učelem není nahradit vlastní zápisky z přednášky, které jsou prostředkem osvojení si studované problematiky.** Tématicky je náplň přednášek pokryta v knize **Stephen G. Kochan: Programming in C (3rd Edition), Sams Publishing, 2005**. Před přednáškou je doporučeno pročíst si odkazované kapitoly. V přednáškách uvedené zdrojové kódy jsou přiloženy v příslušném ''.zip'' archivu. Kromě vyzkoušení programů je též vřele doporučeno si složitější úlohy samostatně naprogramovat a přednáškové příklady využít pro inspiraci. /* **Aktualizováno!** Seznam záznamů přednášek najdete po přihlášení v [[https://cw.felk.cvut.cz/courses/b0b36prp/lectures.html|Upload systému]] nebo na těchto stránkách po přihlášení přístupné [[courses:b0b36prp:resources:records|záznamy přednášek]]. */ /* [[https://docs.google.com/forms/d/e/1FAIpQLSc0PwY1XjEpc0F-3DGXj_S0HPETkX-TMHr9U3kdQjM4VZETUQ/viewform|On-line otázky na přednášce]] */ [[https://docs.google.com/document/d/1CI-W5i3PC4TMv-qxf-WzxyTMniXGNtC7LQZtLxmsSeM/edit#|Otázky na přednášku pište do sdíleného dokumentu]], který je přístupný z domény FEL, tj., použijte svůj přístup do [[http://svti.fel.cvut.cz/cz/services/google-apps.html|Google Apps for Education (GAE)]]. /* [[https://app.sli.do/event/qxncmtrd|On-line otázky na přednášce]] */ ===== Harmonogram přednášek ===== ^ #VTýden ^ Týden ^ Téma ^ Středa 16:15 (Místnost T2:D3-309) ^ | 01 | 38. | [[#1. Informace o předmětu, Procedurální programování základy programování v C|Přednáška 01 - Informace o předmětu, Úvod do programování v C]] \\ S. G. Kochan: kapitoly 2 a 3 | 21.09. - lec01 | | 02 | 39. | //Den české státnosti// | 28.09. | | 03 | 40. | [[#Základy programování v C|Přednáška 02 - Základy programování v C, vstup a výstup programu]]\\ S. G. Kochan: kapitoly 2, 3, 4, 5 a část 6| 05.10. - lec02 | | 04 | 41. | [[#Řídicí struktury, výrazy a funkce|Přednáška 03 - Řídicí struktury, výrazy a funkce]] \\ S. G. Kochan: kapitoly 4, 5, 6 a 12 | 12.10. - lec03 | | 05 | 42. | [[#Pole, ukazatel, textový řetězec|Přednáška 04 - Pole, ukazatel, textový řetězec]] \\ S. G. Kochan: kapitoly 7, 10 a 11 | 19.10. - lec04 | | 06 | 43. | [[#Ukazatele, paměťové třídy a volání funkcí|Přednáška 05 - Ukazatele, paměťové třídy a volání funkcí]] \\ S. G. Kochan: kapitoly 8 a 11 | 26.10. - lec05 | | 07 | 44. |[[#Struktury a uniony, přesnost výpočtů a vnitřní reprezentace číselných typů|Přednáška 06 - Struktury a uniony, přesnost výpočtů a vnitřní reprezentace číselných typů]] \\ S. G. Kochan: kapitoly 9, 14, 17 a Appendix B | 02.11. - lec06 | | 08 | 45. | [[#Standardní knihovny C. Rekurze.|Přednáška 07 - Standardní knihovny C. Rekurze.]] \\ S. G. Kochan: kapitola 16 a Appendix B | 09.11. - lec07 | | 09 | 46. | [[#Spojové struktury|Přednáška 08 - Spojové struktury]]| 16.11. - lec08 | | 10 | 47. | [[#Abstraktní datový typ (ADT) - zásobník, fronta, prioritní fronta|Přednáška 09 - Abstraktní datový typ (ADT) - zásobník, fronta, prioritní fronta]] | 23.11. - lec09 | | 11 | 48. | [[#Stromy|Přednáška 10 - Stromy]]| 30.11. - lec10| | 12 | 49. | [[#Prioritní fronta, halda. Příklad použití prioritní fronty při hledání nejkratší cesty v grafu|Přednáška 11 - Prioritní fronta, halda. Příklad použití při hledání nejkratší cesty v grafu]] | 07.12. - lec11 | | 13 | 50. | [[#Přesnost a rychlost výpočtu|Přednáška 12 - Přesnost a rychlost výpočtu]] | 14.12. - lec12 | | -- | 51. | //Zimní prázdniny// | //19.12.-8.1.// | | -- | 52. | //Zimní prázdniny// | //19.12.-8.1.// | | -- | 01. | //Zimní prázdniny// | //19.12.-8.1.// | | 14 | 02. | [[# C++ konstrukty v příkladech|Přednáška 13 - C++ konstrukty v příkladech ]] //Volitelné téma // | 11.01. - lec13 | /* | 13 | 50. | [[#Stručný úvod do C++|Přednáška 12 - Stručný úvod do C++ ]] //Volitelné téma // | 14.12. - lec12 | | -- | - | [[#Stručný úvod do C++|Stručný úvod do C++ ]] //Volitelné téma // | - | | -- | - | [[# C++ konstrukty v příkladech|C++ konstrukty v příkladech ]] //Volitelné téma // | *./ /* | 14 | 01. | Přednáška 13 - //Rezerva // | 11.01. - lec13 | */ /* //Přednášky jsou řazeny chronologicky za sebou, v případě státního svátku nebo změny v rozvrhu tak nemusí číslo přednášky odpovídat aktuálním výukovému týdnu (#VTýden)// | 14 | 52. | Přednáška 13 - //Rezerva // \\ Dokončení přednášky [[#Stromy|Přednáška 10 - Stromy]] \\ Přehled zdrojových souborů k implementační zkoušce. \\ Dotazy a upřesnění náročnějších pasáží předchozích přednášek a domácích úkolů | 18.12. - lec13 | | 10 | 46. | //Státní svátek// - //Den boje za svobodu a demokracii a Mezinárodní den studentstva// | 17.11. - //Státní svátek// | */ /* //Státní svátek// | 28.10. - //Den vzniku samostatného československého státu// | */ /* **Podklady budou průběžně aktualizovány **: bodové hodnocení, zadání domácích úkolů. */ /* | 13 | 01. | Přednáška 13 - //Rezerva // - Dotazy a upřesnění náročnějších pasáží předchozích přednášek a domácích úkolů - [[#Stromy|Přednáška 10 - Stromy]] | 03.01. - lec13 | */ ===== 1. Informace o předmětu, Procedurální programování základy programování v C ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec01-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec01-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec01-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec01-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec01-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/11/23 14:50// **Doplnění načítání vstupu ** Při načítání vstupu funkcí scanf() lze rozlišit tři případy - úspěšné načtení požadované hodnoty, detekce ukončení vstupu a vstup neodpovídající načítané hodnotě, více viz ''man 3 scanf''. #include int main(void) { int v; int r; int c = 0; while ((r = scanf("%d", &v)) > 0) { c += 1; } if (r == EOF) { printf("End of file detected, no. of parsed values %i\n", c); } else { printf("Error occur during parsing value no. %i\n", c + 1); } return 0; } Při interaktivním vstupu z klávesnice lze vstup ukončit kontrolním znakem ''Ctrl+D'' (nebo ''Ctrl+Z'' na win). ===== 2. Základy programování v C ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec02-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec02-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec02-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec02-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec02-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/10/05 15:12// * **Zdrojové soubory vytvořené na přednášce**: {{courses:b0b36prp:lectures:prp-lec02-codes-2019.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2019/10/02 19:26// **Dotazy z přednášky** Q: //Proč je návratový typ programu (funkce main) ''int'', když maximální hodnota předaná interpretu příkazu pouze 255?// Je to dáno historicky. Cčko nelimituje rozsah a definuje ''int'', nicméně zaleží na procesu, který program spouští. V našem případě interpret příkazů. Dokonce je to tak, že [[https://www.gnu.org/software/bash/manual/bashref.html#Exit-Status|GNU bash limituje rozsah na 7-bitů]], [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02|POSIX shell na 8 bitů]] a třeba [[https://en.wikipedia.org/wiki/Exit_status#Windows|Windows na 32-bit unsigned int]]. V tomto kontextu stojí za zmínku [[http://tldp.org/LDP/abs/html/exitcodes.html|speciální návratové hodnoty]]. Q: //Jaká je vnitřní reprezentace typu _Bool?// Norma standardu C uvádí, že dostatečná pro uložení hodnot 0 a 1. Prakticky je to ''char'', tj. jeden byte, viz příklad ''lec02/bool.c''. Q: //Jak vytisknu znak '%' nebo '\' ve funkci ''printf()''?// Jako dvojici znaků, tj. printf("%% a \\ a následně nový řádek \n"); ===== 3. Řídicí struktury, výrazy a funkce ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec03-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec03-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec03-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec03-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec03-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/10/12 20:33// #define BITS 4 //number of bits to print (4 to make it readable) void print_binary(char *prefix, uint8_t n) { printf(prefix, n); int mask = 1<<(BITS-1); // we need to shift 1 to BITS position, thus -1 for (int i = 0; i < BITS; ++i) { putc((n & mask) ? '1' : '0', stdout); mask = mask >> 1; // } printf("\n"); } uint8_t a = 4; print_binary("a dec: %d bin: ", a); **Dotazy z přednášky** Q: //Proč program i po stísku Ctrl+D dále pokračuje v načítání vstupu?// Je to proto, že Ctrl+D bylo stisknuto pro nějaký rozepsaný vstup, který nebyl programu ještě odeslán klávesou ''Enter'', resp. znakem nového řádku. Vstup programu na ''stdin'' je v našem případě předáván prostřednictvím emulátoru terminálů, který je ve výchozím nastavení řádkově orientovaný, tj. náš vstup je programu předám po stisku klávesy ''Enter''. Stiskem kombince ''Ctrl+D'' vyvoláme předání aktuálního vstupu programu. Pokud je aktuální vstup prázdný, generuje terminál ''EOF'' (End Of File), standardní vstup je uzavřen a následně čtení např. funkcí ''scanf()'' nebo ''getchar()'' vrací hodnotu EOF (-1). Více informací lze nalézt např. v manuálové stránce [[https://www.freebsd.org/cgi/man.cgi?query=termios&sektion=4|termios]] v části //Special Characters// znak ''EOF''. |''EOF'' | //Special character on input and is recognized if the ICANON flag is set. When received, all the bytes waiting to be read are immediately passed to the process, without waiting for a newline, and the EOF is discarded. Thus, if there are no bytes waiting (that is, the EOF occurred at the beginning of a line), a byte count of zero is returned from the read(), representing an end-of-file indication. If ICANON is set, the EOF character is discarded when processed.// | Q: //Jak mohu porovnat dva soubory?// Kromě použítí příkazu ''diff'' existují další nástroje, např. [[https://meldmerge.org/]], případně konzolový ''vimdiff'' nebo grafický ''gvimdiff'' editoru [[https://www.vim.org/|vim]], více např. [[https://www.zdrojak.cz/clanky/vimdiff-nastroj-drsnaku/|vimdiff-nastroj-drsnaku]]. Q: //Proč kompilátor hlásí chybu chybějícího znaku nového řádku na konci souboru?// Při kompilaci programu bez znaku konce řádku na konci souboru, např. při kompilaci ''clang -pedantic -Wall -Werror main.c'', dojde k chybě. error: no newline at end of file [-Werror,-Wnewline-eof] Je do dáno [[https://gcc.gnu.org/legacy-ml/gcc/2003-11/msg01568.html|historickým rozhodnutím]], že neprázdný zdrojový soubor by měl být zakončem znakem nového řádku. Je tak součástí standardu jazyka. Dobrý důvod proč je vhodné zakončovat soubory koncem řádku je například při použit vložení souborů příkazem preprocesoru ''#include'', který nevkládá nový řádek, přestože to mnoho moderních kompilátorů dělá. Stále však platí, že starší kompilátory s tím mohou mít potíže a může to být velmi těžko odhalitelná chyba. V novém C%%++%%11, již tento požadevek není, stále však zůstává pro C99. V případě VS Code, podobně jako v celé řadě jiných programů, lze chování editor nastavit, např. [[https://stackoverflow.com/questions/44704968/visual-studio-code-insert-new-line-at-the-end-of-files|Insert New Line at the End of Files]]. Q: //Je možné umístit návěští pro příkaz ''goto'' před cyklus?// V zásadě ano, jediným zásadním omezením je pouze v rámci jedné funkce. Dále je nutné, aby za návěštím byl příkaz, například definice proměnné není příkaz. Musí existovat adresa instrukce, na kterou má ''goto'' skočit. ^ Návěští před začátkem cyklu ^ Návěští před začátkem cyklu před definicí proměnné (**nelze**)^ Návěští před začátkem cyklu s příkazem ^ | int a; // začátek platnosti a outer: for (int i = 0; i < 3; ++i) { goto outer; } | outer: // definice proměnné není příkaz int a; for (int i = 0; i < 3; ++i) { goto outer; } | outer: // po návěští je příkaz ; int a; for (int i = 0; i < 3; ++i) { goto outer; } | Nicméně podstatnou nevýhodou umístění návěští před cyklus je horší čitelnost a tím také vyšší náchylnost k chybě a zacyklení. ===== 4. Pole, ukazatel, textový řetězec ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec04-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec04-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec04-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec04-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec04-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/10/19 20:15// --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/10/19 20:15// **Aktualizace**: Oprava slide 28 (návratová hodnota) a slide 38 (pořadí argumentů). **Dotazy z přednášky** Q: //Jak inicializovat hodnoty pole při definici?// Při definici proměnné typu pole můžeme rovnou nastavit jednotlivé prvky a využít tzv. //designated initializers//. V takovém případě jsou incializované všechny prvky. Ty uvedené na zadanou hodnotu, jinak je nastavena výchozí hodnota 0. int a[6] = {10, 12, [3] = 3, 5}; //pole a má 6 prvků s hodnotami {10, 12, 0, 3, 5, 0}, tj. nespecifkované prvky mají hodnotu 0. int b[] = {1, 2, 3}; // pole má pouze 3 prvky {1, 2, 3} int c[6] = {1, 2, 3}; // pole má 6 prvků { 1, 2, 3, 0, 0, 0} int d[4] = {}; // pole má 4 prvky incializované na 0, tj. {0, 0, 0, 0} Q: //Proč uvedený příklad načítání skončil v nekonečné smyčce?// Načítání hodnot celých čísel funkcí ''scanf()'' ve smyčce ''while'': #include int main(void) { int v; int i = 0; while (scanf("%d", &v) > 0) { printf("%d %d\n", ++i, v); } printf("No. of read values %d\n", i); return 0; } Skončil v nekonečné smyčce, neboť původní podmínka cyklu while (scanf("%d", &v)) { ... } je vyhodnocena jako logická pravda i v případě neúspěšného načtení nové celočíselné hodnoty. V proměnné zůstane původní hodnota. Při opravě jsem, jak někdo na přednášce zmínil, zapomněl soubor uložit. ===== 5. Ukazatele, paměťové třídy a volání funkcí ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec05-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec05-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec05-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec05-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec05-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/10/26 21:26// --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/10/26 18:0// **Aktualizace**: Oprava slide 16, ''void free(pointer);''. **Dotazy z přednášky** Q: //Snažím se načíst celé číslo do typu ''unsigned int'' funkcí ''scanf()'' a když dám na vstup celé záporné číslo, např. -10, nejen, že se hodnota načte, ale hodnota je veliká. Jak je to možné?// Minimální kód programu ''read_unsigned.c'' může být například: #include int main(void) { unsigned int i; int r = scanf("%+u", &i); printf("Return value %i read value %u\n", r, i); return 0; } Výstup pak například clang read_unsigned.c && echo "-10" | ./a.out Return value 1 read value 4294967286 Hodnota 4294967286 odpovídající maximálním hodnotě typu ''unsigned int'' bez 10 vyplývá z použití doplňkového kódu. Program nemá zřejmé očekávané chování. Při bližším pohledu na chování funkce ''scanf()'', např. ''man scanf'' NAME scanf, fscanf, sscanf, vscanf, vsscanf, vfscanf — input format conversion LIBRARY Standard C Library (libc, -lc) ... u Matches an optionally signed decimal integer; the next pointer must be a pointer to unsigned int. ... se dočteme, že konverze přes ''"%u"'' načítá znamenkový ''integer''. Funkce se tak chová dle dokumentace. V případě, že bychom se chtěli vyhnout takovému chování můžeme buď použití větší datový typ, např. ''unsigned long'', a kontrolovat rozsah pro ''unsigned int'' nebo si napsat vlastní načítání pouze kladných hodnot a při výskytu znaku '''-''' na vstupu indikovat špatný vstup. ===== 6. Struktury a uniony, přesnost výpočtů a vnitřní reprezentace číselných typů ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec06-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec06-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec06-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec06-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec06-codes.zip|}} — Jan Faigl 2021/11/07 08:48 **Aktualizace**: Přidání příkladu reprezentace hodnot typu ''float''. --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/11/02 14:48// **Doplnění k HW06 "přetypování" dynamického pole na statické pole** V domácím [[courses:b0b36prp:hw:hw06|úkolu 6]] na matice je **povinné zadání** možné realizovat pouze s využitím proměnných typu pole (VLA). V zásadě nám stačí postupně načíst dvě matice a spočítat výsledek. Matice můžeme načíst tak, že nejdříve načteme rozměry matice a následně alokujeme matici na zásobníku jako dvourozměrné VLA pole. Problém tak dekomponujeme na funkce pro * Načtení rozměrů matice * Načtení matice danných rozměrů * Tři funkce pro součet, rozdíl a násobení. Celkově je implementace do cca 150 řádků a méně. Nemusíme řešit alokaci/dealokaci a procvičíme si práci s polem VLA. Při implementaci volitelného zadání vystačíme se čtyřmi proměnnými pro matice a postupně načítáme matice a co je možné spočítat můžeme rovnou vyčíslit, např. v případě tří matice v součtu můžeme první dvě sečít, v případě násobení můžeme dvě matice rovnou vynásobit. Nicméně zde se nám už může hodit dynamická alokace a funkce pro načtení matice nám přímo alokuje paměť na haldě což je pohodlnější, jen pak nesmím zapomenout na uvolnění paměti voláním ''free()''. Jestliže jsme použili pro povinné zadání VLA pole, nemusíme funkce přepisovat, ale můžeme využít faktu, že pole i vícerozměrné je souvislý blok paměti. Proto pokud alokujeme matici dynamicky jako souvislý blok paměti o velikosti počet řádku krát počet sloupců můžeme využít faktu, že proměnná pole ukazuje na první prvek podobně jako hodnota ukazatele dynamického pole. void print(int rows, int cols, int m[cols][rows]) { for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { printf("%4d", m[r][c]); } print("\n"); } } ... int *matrix_p = malloc(cols * rows * sizeof(int)); int (*matrix)[cols] = (int (*)[cols]) matrix_p; print(rows, cols, matrix); // s proměnnou matrix můžem pracovat jako s dvojrozměrným polem free(matrix_p); Výhoda tohoto přístupu je, že použijeme většinu implementace z předchozího řešení povinného zadání. Celkově se implementace vejde do 180 řádků. V případně bonusové úlohy, ale také i volitelného zadání lze s výhodou použít složený typ ''struct'', který bude obsahovat položky definující velikost matice a vlastní data. V tomto případě se dynamické alokaci nevyhneme, proto může být výhodné kromě struktury, implementovat též funkce pro alokaci případně dealokaci. Můžete si též vyzkoušet dynamickou alokaci jako jednorozměrnné pole (souvislý blok paměti) nebo dynamickou alokaci dvourozměrného pole jako pole ukazatelů na dynamické pole hodnot. V tomto případě však nebude matice reprezentována jako souvislý blok paměti a celkově je potřeba také více paměti, tj. ''rows * sizeof(int*) + rows * cols * sizeof(int)'', protože pole řádku je pole ukazatelů na int. **Dotazy z přednášky** Q: //Je nutné nebo vhodné explicitně typovat ukazatel návratové hodnoty z volání funkce ''malloc''()?// Vyloženě nutné to v současných verzích Cčka není, přestože pro některé kompilátory (zvláště pak před standarem) to nutné bylo. V současné době je typ ''void*'' chápan jako generický ukazatel, jehož přetypování na konktrétní typ ukazatel na proměnné příslušné typu je zřejmé dle typu proměnné a není tak nutné explicitní přetypování uvádět. Jestli je vhodné explicitně přetypovat, tak na to se názory různí. Například v knize //S.G.Kochan: Programming in C (3rd Edition), Sams Publishing, 2005// je uváděn ''malloc'' vždy s explicitním přetypováním: int *a = (int*)malloc(10 * sizeof(int)); Naproti tomu v knize K//.N. King: C Programming A Modern Approach, Second Edition. W. W. Norton & Company, Inc., 2008// je preferována varianta bez přetypování: int *b = malloc(10 * sizeof(int)); Obě varianty jsou přípustné, argumenty proti explicitnímu přetypování jsou uváděny například: přehlednější kód a je to zbytečné, neboť dochází k přetypování automaticky. Na druhé straně relativně silné argumenty pro explicitní přetypování uvedené v diskusi [[http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc]] jsou například: You do cast, because: * It makes your code more portable between C and C++, and as SO experience shows, a great many programmers claim they are writing in C when they are really writing in C++ (or C plus local compiler extensions). * Failing to do so can hide an error: note all the SO examples of confusing when to write type * versus type **. * The idea that it keeps you from noticing you failed to #include an appropriate header file misses the forest for the trees. It's the same as saying "don't worry about the fact you failed to ask the compiler to complain about not seeing prototypes -- that pesky stdlib.h is the REAL important thing to remember!" * It forces an extra cognitive cross-check. It puts the (alleged) desired type right next to the arithmetic you're doing for the raw size of that variable. I bet you could do an SO study that shows that malloc() bugs are caught much faster when there's a cast. As with assertions, annotations that reveal intent decrease bugs. * Repeating yourself in a way that the machine can check is often a great idea. In fact, that's what an assertion is, and this use of cast is an assertion. Assertions are still the most general technique we have for getting code correct, since Turing came up with the idea so many years ago. Je to tak spíše věc osobního vkusu, preferencí, případně používaného kódovacího stylu. Q: //Jak funguje ukazatelová aritmetika v případě void*?// Přičítá se hodnota jednoho bajtu, například pro hodnotu ukazatele ''void *a = 0x100;'' bude je hodnota ''(a+1)'' rovna 0x101. Q: //Proč se říká, že reprezentace 0.1 má největší chybu?// V případě reprezentace dle IEEE-754 je strojová přesnost dána rozdílem mezi 1 a dalším reprezentovatelným číslem, která je 2^{-52}, která je v knihovně '''' definována jako symbolická konstanta ''DBL_EPSILON''. S využitím např. [[https://www.h-schmidt.net/FloatConverter/IEEE754.html]] uvidíme, že hodnota 0.1 je binárně reprezentovaná jako ''0-01111011-10011001100110011001101'' (znaménko 1 bit, exponent 8 bitů a mantisa 23 bitů), což odpovídá nejbližšímu reprezentovatelnénu číslu ''0.100000001490116119384765625''. Chyba reprezentace je tak ''1.490116119384765625E-9''. Například v případě hodnoty 0.15 je chyba reprezentace ''5.9604644775390625E-9'', takže chyba reprezentace 0.1 není zas tak zásadně velká. Spíše může být překvapivé, proč číslo 0.1 má takovou chybu, když reprezentujeme hodnotu mantisou a exponentem. Důvod hledejme v reprezentaci v soustavě o základu 2. Binární reprezentaci hodnotu ''0.1'' najdeme dělením 2, jenže brzo zjistíme, že v rozvoji se opakuje 0011, takže přesnost reprezentace je dána počtem bitů v mantise. Proto ''0.1'' bude reprezentovan0 jinak v případně ''float'' než ''double''. Obecně tak při přetypování dojde k chybě a proto není příliš moudré spoléhat na porovnání hodnot necelých čísel operátorem ''==''. Na druhou stranu, pokud do jedné proměnné ''double'' a do druhé hodnoty ''double'' přiřadíme stejné číslo, tak pochopitelně bude binární reprezentace identická a operátor ''=='' zafunguje podle očekávání. Více o reprezentaci číselných hodnot čekejte v předmětu [[https://cw.felk.cvut.cz/wiki/courses/b35apo/|APO]]. Převod 0.1 do dvojkové soustavy (desetiná čísla dělíme 2^{-i} kde i je desetinná část což odpovídá násobení 2). 0.1 * 2 = 0.2 -> 0 0.2 * 2 = 0.4 -> 0 0.4 * 2 = 0.8 -> 0 0.8 * 2 = 1.6 -> 1 0.6 * 2 = 1.2 -> 1 0.2 * 2 = 0.4 -> 0 0.4 * 2 = 0.8 -> 0 0.8 * 2 = 1.6 -> 1 0.6 * 2 = 1.2 -> 1 0.2 * 2 = 0.4 -> 0 ... Z převodu vidíme, že 0.1 odpovídá 1.6*2^{-4} a také to, že se opakuje vzor 0011, tedy číslo 0.1 ve dvojkové soustavě nereprezentujeme přesně na konečný počet digitů. ===== 7. Standardní knihovny C, rekurze ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec07-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec07-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec07-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec07-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec07-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2021/12/30 20:32// **Aktualizace**: Oprava popisu návratové hodnoty ''fread'' a ''fwrite''. --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/11/08 20:50// ** Umístění errno.h ** Chybové kódy standardní knihovny C jsou definovány v souboru ''errno.h'', umístění souboru s definicí chyb je však na různých systémch různé. V unixovových systém se jedná zpravidla o ''/usr/include/errno.h'' případně ''/usr/include/sys/errno.h'', popis jednotlivých konstant a hodnot lze také najít např. v ''man errno''. V distribucích Linuxu je situace trošku složitější, neboť umístění záleží na konkrétní distribuci, např. lP5oGIqdWEU * /usr/include/asm/errno.h * /usr/include/asm-generic/errno-base.h * /usr/include/asm-generic/errno.h * /usr/lib/bcc/include/linux/errno.h * /usr/include/sys/errno.h * /usr/include/asm-i386/errno.h Konkrétní soubor můžeme najít například příkazem grep -R 17 /usr/include/**/errno*.h /usr/include/asm-generic/errno-base.h:#define EEXIST 17 /* File exists */ /usr/include/asm-generic/errno.h:#define EUCLEAN 117 /* Structure needs cleaning * jehož výstup nám napoví, že na tomto konkrétním Linuxu jsou rozděleny chybové kódy do souborů ''errno-base.h'' (obsahující základní kódy) a ''errno.h'' obsahující rozšířené kódy, tj. kódy s vyšší hodnotou. Dále je většinou v Linuxu standardní knihovna libc realizována jako ''GNU libc'' ([[ https://www.gnu.org/software/libc/|glibc ]]), což přináší některé [[https://www.gnu.org/software/libc/manual/ | zajímavé funkce ]], které na druhou stranu nejsou dostupné na jiných OS, nelinuxového typu, např. OS X. Q: Je nějaké omezení na hloubku rekurzivního volání? Ano, při volání funkce se minimálně ukládá na zásobník adresa odkud funkci voláme, aby mohlo dojít k návratu z volání funkce. Dále se při každém volání mohou vytvářet na zásobníku lokální proměnné. Tedy omezení je dáno velikostí zásobníku. Omezení si můžeme vyzkoušet následujícím programem. #include void printValue(int v); int main(void) { printValue(1); } void printValue(int v) { printf("value: %i\n", v); printValue(v + 1); } a spuštěním s omezenou velikostí zásobníku ulimit -s 1000; ./a.out | tail -n 3 value: 31730 value: 31731 Segmentation fault a s větší velikostí. ulimit -s 10000; ./a.out value: 319816 value: 319817 Segmentation fault V obou případech končí volání chybou. Ve druhém případě došlo k hlubšímu zanoření. Zotavení z chybějícího místa na zásobníku je velmi náročné, narozdíl od dynamické alokace. Výchozí hodnota zásobníku je nastavana na relativně dostačující velikost pro běžné programy. Nicméně v případě rekurzivního řešení je dobré pamatovat, že místo na zásobníku není neomezené. Proto je vždy vhodné rozumět úloze a konkrétním (typickým) vstupům a prostředí. Například na základě velikosti vstupu můžeme odhadnout, potřebnou velikost zásobníku a případně uživatele informovat. Asi nejhorší případ je, když program padá a netušíme proč tomu tak je. ===== 8. Spojové struktury ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec08-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec08-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec08-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec08-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec08-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/11/23 14:47// Jak úsporně napsat dynamickou alokaci bez dvojího zápisu typu proměnné od Ondřeje T. int *array = malloc(100 * sizeof *array); Využíváme faktu, že ''sizeof'' je operátor a zároveň, že jej můžeme použít přímo s proměnnou. Zde ''*array'' je typu int. ===== 9. Abstraktní datový typ (ADT) - zásobník, fronta, prioritní fronta ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec09-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec09-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec09-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec09-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec09-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/11/23 14:47// ===== 10. Stromy ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec10-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec10-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec10-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec10-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec10-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/11/23 14:47// while (!feof(f) && !exit) { -> while (!exit) { if (g->num_edges == g->capacity) { if (g->num_edges == g->capacity) { enlarge_graph(g); enlarge_graph(g); } } edge_t *e = g->edges + g->num_edges; edge_t *e = g->edges + g->num_edges; while (!feof(f) && g->num_edges < g->capacity) { -> while (g->num_edges < g->capacity) { Funkce ''feof()'' vrací logickou hodnotu ''TRUE'' pouze v případě dosažení konce souboru, což je detekováná až při pokusu o čtení. Vždy je proto nutné kontrolovat návratové hodnoty čtecích funkcí více např. viz [[https://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1046476070&id=1043284351]] ===== 11. Prioritní fronta, halda. Příklad použití prioritní fronty při hledání nejkratší cesty v grafu ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec11-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec11-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec11-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec11-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec11-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/12/07 15:17// --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/12/10 23:30// **Aktualizace**: Přidání tdijkstra binárky pro OS X (intel and arm). --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2017/12/23 08:09// **Aktualizace**: Zjednoušení návratové hodnoty funkce ''dijkstra_solve()'' a zpřehlednění podmínky v ''pq_down()''. --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2018/01/08 08:48// **Aktualizace**: tdijkstra verze 2.3.6. - fix segfault při pokusu testovat řešení většího grafu než je vstupní graf --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2018/01/08 08:48// **Aktualizace**: tdijkstra verze 2.3.7. - fix návrat 0 při chybě --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/12/03 23:48// **Dotazy z přednášky** Q: //Jak se používá ''fflush()''?// Funkce ''int fflush(FILE *stream)'' slouží k předání dat bufferu (vyrovnávací paměti) operačnímu systému (OS), např. při nastavení bufferu souboru (streamu/proudu) funkcí (''setbuf()''). Nemění stav souboru, data jsou předána nižší zapisovací funkci. OS může data dále bufferovat. Volání funkce ''int fsync(int fd)'' z knihovny ''unistd.h'' je systémové volání, které dává pokyn OS, aby data byla zapsaná na fyzické zařízení. Funkce pracuje s ''fd'' -- //celočíselným file deskriptorem//, který je možné získat z proudu funkcí ''int fileno(FILE *stream)'' ze standardní knihovny ''stdio.h''. ===== 12. Přesnost a rychlost výpočtu ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec12-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec12-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec12-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec12-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec12-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/12/14 13:16// ==== Volitelná témata ==== ===== 12cc. Stručný úvod do C++ ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec12cc-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec12cc-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec12cc-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec12cc-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec12cc-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/09/08 17:17// ===== 13cc. C++ konstrukty v příkladech ===== * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec13cc-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec13cc-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec13cc-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec13cc-handout-3x3.pdf|}} * přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec13cc-codes.zip|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/09/08 17:17// ===== 14. Přednáška na vyzvané téma ===== Zkouškový test Např. systémy pro správu verzí * prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec14-slides.pdf|}} * zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec14-handout.pdf|}} * zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec14-handout-2x2.pdf|}} * zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec14-handout-3x3.pdf|}} --- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2022/09/08 17:17//