Podklady přednášek pro aktuální akademický rok 2019/2020. 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é slouží také jako prostředek osvojení si studované problematiky.
Tématicky je náplň přednášek pokryta v knize K.N. King: C Programming A Modern Approach, Second Edition. W. W. Norton & Company, Inc., 2008/. Před přednáškou je doporučeno pročíst si odkazované kapitoly.
#VTýden | Týden | Téma | Úterý 14:30 (Místnost T2:D3-209) |
---|---|---|---|
01 | 08. | Přednáška 01 - Informace o předmětu, Úvod do programování v C K. N. King: kapitoly 1, 2 a 3 | 18.02. - lec01 |
02 | 09. | Přednáška 02 - Výrazy, řídicí struktury a funkce K. N. King: kapitoly 4, 5, 6 a 20 | 25.02. - lec02 |
03 | 10. | Přednáška 03 - Datové typy: pole a ukazatele. Paměťové třídy. Volání funkcí K. N. King: kapitoly 7, 8, 9, 10, 11 a 18 | 03.03. - lec03 |
04 | 11. | Přednáška 04 - Pole, řetězce a ukazatele K. N. King: kapitoly 8, 11, 12, 13 a 17 | 10.03. - lec04 |
05 | 12. | Přednáška 05 - Datové typy: Složený typ, výčtový typ a bitová pole. Preprocesor a sestavení programu K. N. King: kapitoly 10, 14, 15, 16 a 20 | 17.03. - lec05 |
06 | 13. | Přednáška 06 - Vstup / výstup a standarní knihovny C K. N. King: kapitoly 21, 22, 23, 24, 26 a 27 | 24.03. - lec06 |
07 | 14. | Přednáška 07 - Paralení programování, paralelní výpočty a synchronizační primitiva (semafory, zprávy a sdílená paměť ) | 31.03. - lec07 |
08 | 15. | Přednáška 08 - Vícevláknové programování, modely aplikací, POSIX vlákna C11 vlákna | 07.04 - lec08 |
09 | 15. | Přednáška 09 - Praktická ukázka a ladění programu | 14.04. - lec09 |
10 | 17. | Přednáška 10 - ANSI C, C99, C11 a rozdíly mezi C a C++. Úvod do C++ | 21.04. - lec10 |
11 | 18. | Přednáška 11 - Stručný úvod do C++ | 28.04. - lec 11 |
12 | 19. | - | 05.05. páteční rozvrh |
13 | 20. | Přednáška 12 - C++ konstrukty v příkladech | 12.05. |
14 | 21. | Přednáška 13 - Zkouškový písemný test | 19.05. - lec 13 |
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.
— Jan Faigl 2020/03/05 23:31
— Jan Faigl 2020/02/12 15:35
Na přednášce zmíněná funkce fgets()
pro řešení HW 03 nemusí být zas až tak výhodná a mnohem výhodnější může být využití funkce getline()
, která však pracuje pouze s dynamicky alokovanou pamětí.
— Jan Faigl 2020/02/12 15:35
— Jan Faigl 2020/02/12 15:35
Příklad z přednášky
Příklad na přednášce demonstrující použití kompilace s debug, např. clang -g
a valgrind
pro rychlé nalezení problémového místa nám pomohl ukázat na zápis mimo přidělenou paměť na řádku 13, tj. sieve[i*j] = 0;
10 for (int i = 2; i < 1000000; ++i) { 11 if (sieve[i]) { 12 for (int j = i; i*j < 1000000; ++j) { 13 sieve[i*j] = 0; 14 } 15 } 16 }
Přestože řádek vypadá relativně neškodně, je nutné uvažovat jakou hodnotu bude mít výraz i*j
pokud jsou obě proměnné typu int
. Zcela jistě se totiž hodnota výrazu, např. 47000 * 47000, do rozsahu typu int
reprezentovaného 32-bity nevejde.
Možných řešení je více, jedno z nich může být využít automatické typové konverze na větší z typů, např.
10 for (int i = 2; i < 1000000; ++i) { 11 if (sieve[i]) { 12 for (long j = i; i*j < 1000000; ++j) { 13 sieve[i*j] = 0; 14 } 15 } 16 }
Rozdíl je na řádku 12 v použití typu long
pro řídicí proměnnou j
. V tomto případě je hodnota výrazu i*j
typu long
a problém je vyřešen.
Každopádně mnohonásobné použití magické konstanty 1000000
lze vhodně nahradit např.
#define SIEVE_SIZE 1000000
Dotazy z přednášky
Q: Co udělá realloc()
při zmenšení velikost nebo nastavení velikosti na 0?
Zmenší alokované místo a v případě 0, např. realloc(ptr, 0);
uvolní paměť a odpovídá tak volání free(ptr);
.
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:
Je to tak spíše věc osobního vkusu, preferencí, případně používaného kódovacího stylu.
— Jan Faigl 2019/03/05 13:27
— Jan Faigl 2020/02/12 15:35
Dotazy z přednášky
Q: Jakým způsobem mohou přetypovat dynamicky alokovaný souvislý blok paměti na dvourozměrné pole o cols
sloupcích?
Předně je nutné zajistit, že ukazatel odkazuje na souvislý blok paměti o příslušné velikost. Následně lze definovat proměnnou se specifikací počtu sloupců např. int(*matrix)[cols] = (int(*)[cols])m;
, kde m
je ukazatel na souvislý blok int
hodnot. V kontextu programu to může vypadat, následovně.
void *alloc(size_t size) { void *ret = malloc(size); if (ret == NULL) { fprintf(stderr, "Malloc fail!"); exit(-1); } return ret; } void print_matrix(int rows, int cols, int m[][cols]) { for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { printf("%2d ", m[r][c]); } printf("\n"); } } int rows = 2; int cols = 3; int *m = alloc(rows * cols * sizeof(int)); for (int i = 0; i < rows * cols; ++i) { m[i] = i + 1; } int(*matrix)[cols] = (int(*)[cols])m; print_matrix(rows, cols, matrix); free(m);
Můžeme toho například využít při řešení HW05, kdy si nejdříve odladíme funkce pro dvojrozměrné pole, které následně můžeme přímo využít pro rozšířenou implementaci s využitím dynamické alokace.
Q: Je nutné v případě dynamicky alokované matice, jako pole ukazatelů na pole hodnot, postupně paměť uvolňovat podobně jako při alokaci?
Ano je. Nejdříve je nutné postupně uvolnit dílčí pole reprezentující sloupce v jednotlivých řádcích a následně pak uvolnit pole ukazatelů (na řádky). S výhodou je možné si definovat funkce pro alokaci a uvolnění paměti, např.
void *alloc(size_t size) { void *ret = malloc(size); if (ret == NULL) { fprintf(stderr, "Malloc fail!"); exit(-1); } return ret; } int **matrix_alloc(int rows, int cols) { int **matrix = alloc(rows * sizeof(int *)); for (int r = 0; r < rows; ++r) { matrix[r] = alloc(cols * sizeof(int)); } return matrix; } void matrix_free(int rows, int cols, int **matrix) { if (matrix) { for (int r = 0; r < rows; ++r) { if (matrix[r]) { free(matrix[r]); } } free(matrix); } }které můžeme následně použít například
const int ROWS = 2; const int COLS = 2; int **matrix = matrix_alloc(ROWS, COLS); matrix_free(ROWS, COLS, matrix);
Pragmatičtější však může být použití složeného typu struct
.
— Jan Faigl 2019/03/19 22:46
— Jan Faigl 2020/02/12 15:35
Dotazy z přednášky
Q: Jakým funguje Quick-Sort?
Jedná se o algoritmus řazení na principu rozděl a panuj, který je například popsán na https://cs.wikipedia.org/wiki/Quicksort nebo znázorněn na Quick-sort with Hungarian (Küküllőmenti legényes) folk dance.
— Jan Faigl 2020/02/12 15:35
— Jan Faigl 2018/05/16 07:54 CEST Aktualizace: Oprava output_thread() - zamčení/odemčení mutexu vně cyklu
— Jan Faigl 2020/02/12 19:49
Přednáška formou ukázek.
— Jan Faigl 2018/02/19 16:00
Dotazy z přednášky
Q: Proč při nastavení terminálu do raw režimu nedochází k tisku nového řádku na začátku?
Při nastavení raw režimu cfmakeraw()
je vypnuto zpracování výstupních znaků (output postprocessing), které automaticky nahrazuje konec řádku za “návrat vozíku a nový řádek” (NL na CR-NL). Vyřešit lze buď explicitním uvedením znaku '\r', což ale není příliš kompatibilní nebo povolením output processing (OPOST) např.
static struct termios tio; tcgetattr(STDIN_FILENO, &tio); cfmakeraw(&tio); // nastavení raw režimu viz man termios tio.c_oflag |= OPOST; //zapnutí output processing tcsetattr(STDIN_FILENO, TCSANOW, &tio);
— Jan Faigl 2020/02/12 19:48
Dotazy / připomínky z přednášky
C: Při přepnutí terminálu do raw režimu příkazem stty raw
stále dochází k tisku stisknuté klávesy
Pokud tomu tak je (např. v Linuxu), je nutné ještě vypnout echo například stty -noecho
příkaz pro nastavení termínálu tak může být
stty raw -echo
a nastavit zpět pak lze např.
stty -raw echo
— Jan Faigl 2020/02/12 19:48
— Jan Faigl 2020/02/12 19:48
Původní podklady pro obecný úvod do verzovacích systémů a to jak centralizovaných tak distribuovaných, s příkladem použití systémů RCS, SVN a Git.
— Jan Faigl 2018/02/19 16:00