Přednášky

Podklady přednášek pro aktuální akademický rok 2020/2021. 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.

Otázky k předmětu a přednášce

#VTýden Týden Téma Středa 16:15 (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
17.02. - lec01
02 09. Přednáška 02 - Výrazy a řídicí struktury
K. N. King: kapitoly 4, 5, 6 a 20
24.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++ (v příkladech) 28.04. - lec11
12 19. Přednáška 12 - Přehled C++ konstruktů 05.05. - lec12
13 20. Rezerva 12.05. (Rektorský den)
14 21. Přednáška 13 - Systémy pro správu verzí Přednáška 14 - Rezerva Zkouškový písemný test Objekty v C++ 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.

Podklady budou průběžně aktualizovány.

1. Informace o předmětu, základy programování v C

Jan Faigl 2021/02/17 08:03

2. Výrazy a řídicí struktury

Jan Faigl 2021/02/18 23:41

3. Datové typy: pole a ukazatele. Paměťové třídy. Volání funkcí

2020: 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 2021/02/11 23:25

4. Pole, řetězce a ukazatele

Jan Faigl 2020/03/13 21:45

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:

  • 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.

Jan Faigl 2019/03/05 13:27

5. Datové typy: Složený typ, výčtový typ a bitová pole. Preprocesor a sestavení programu

Jan Faigl 2021/03/17 14:20

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

6. Vstup / výstup a standarní knihovny C

Jan Faigl 2020/03/25 21:57

Dotazy z přednášky

Q: Jak 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.

7. Paralení programování, paralelní výpočty a synchronizační primitiva

Jan Faigl 2020/04/03 09:00

8. Vícevláknové programování, modely aplikací, POSIX vlákna C11 vlákna

Jan Faigl 2018/05/16 07:54 CEST Aktualizace: Oprava output_thread() - zamčení/odemčení mutexu vně cyklu

Jan Faigl 2020/04/07 17:32 CEST Aktualizace: Odstranění globálních proměnných

Jan Faigl 2021/04/07 15:45

9. Praktická ukázka a ladění programu

Přednáška formou ukázek.

Jan Faigl 2020/04/18 15:55

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);

10. ANSI C, C99, C11 a rozdíly mezi C a C++. Úvod do objektově orientovaného programování v C++

Jan Faigl 2020/05/02 11:31

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

11. Stručný úvod do C++ (v příkladech)

Jan Faigl 2021/04/28 18:42

12. Přehled C++ konstruktů

Jan Faigl 2021/05/05 18:14 Aktualizace: Oprava příkladu na virtuální destruktor

13. Systémy pro správu verzí

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, Git a SVN.

Jan Faigl 2021/05/03 09:30

courses/b3b36prg/lectures/start.txt · Last modified: 2021/05/11 21:50 by faiglj