{{indexmenu_n>3}}
====== Lab 03 - Dekompozice programu ======
* Pro vyučující: [[courses:b3b36prg:internal:tutorialinstruction:03|]].
{{ :courses:b3b36prg:labs:prg-lab03.zip |Výchozí soubory prg-lab03.zip}} //(Pokročilé úkoly)//
===== Cíle cvičení =====
- Podmínky a cykly ([[courses:b3b36prg:tutorials:coding:control|řídicí struktury]]), [[https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B|operátory]].
- Implementace funkčního a správného programu s ošetřením možných chybových stavů.
- Dekompozice programu na funkce.
- Ladění programu.
===== Úkoly =====
==== Aplikace Trojúhelník ====
Implementujte program, který přečte ze standardního vstupu celé číslo, které by mělo být v rozsahu 1 až 10 a vytiskne následující trojúhelník, např. pro n=4.
****
***
**
*
=== Zpracování a kontrola uživatelksého vstupu ===
Při implementaci funkční program postupně rošiřujte o ošetření chybných vstupů, reportování chyb, případně dekompozici tisknutí trojúhelníku na tisknutí řádků, např. v následujících krocích.
- Implementace základní trivální implementace s využitím ''scanf()'' a dvou (vnořených) for cyklů s ''putchar('*')'' a odřádkováním ''putchar('\n')'';
- Testujte chování programu pro špatný vstup, např. ''clang main.c && echo "1" | ./a.out ; echo $?''.
- Rozšiřte program o kontrolu rozsahu načtené hodnoty a testování návratové hodnoty funkce ''scanf()'' (viz ''man scanf''), např. pro neceločíselný vstup nebo prázdný vstup (např. přesměrovaný ze souboru ''touch in.txt; ./a.out < in.txt'' nebo ukončení vstupu ''CTRL+D'').
- Rozšiřte program o reportování chybného vstupu návratovou hodnotou programu ''100'' a ''101'' s výstupem.
* 100 - ''"ERROR: Cannot read integer value from the standard input\n"''
* 101 - ''"ERROR: Given value %d is not within the range [1, 10]\n"''
- Hodnoty rozsahu 1 a 10 nahraďte definovanými makry ''MIN_VALUE'' a ''MAX_VALUE'' s možností ovlivnění maximální hodnoty při kompilaci , např. ''clang -DMAX_VALUE=100''. Otestujete chování programu pro ruzné vstupy a kompilace s různými hodnotami.
=== Dekompozice programu do funkcí ===
- Dekomponujte tisk trojúhelníku na funkce
* ''void print_triangle(char c, int lines);'' a
* ''void print_line(char c, int n);''
- Zjednodušte hlavní funkci definováním funkcí
* ''int read_input(int *n);'' a
* ''int print_error(int ret, int n);''
- Upravte program pro tisk chybových zpráv na ''stderr'' funkcí ''fprintf()'' a otestujete chování program s přesměrováním vstup a výstup, např. ''./main out.std 2> out.err''.
- Program rozšiřte o zpracování argumentů programu ''argc'' a ''argv'' s využitím funkce ''sscanf()''.
{{:courses:b3b36prg:internal:tutorialinstruction:triangle.png?1000|}}
==== Ladění programu - Debug ====
* Všechny programy v této sekci kompilujte s přepínačem ''-g''.
* Spusťte následující program. Porovnejte výstup s ostatními ve třídě a vysvětlete, co se v programu děje.
#include
int main(int argc, char *argv[])
{
int arr[5] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < 30; ++i){
printf("%d: %d\n", i, arr[i]);
}
return 0;
}
* Zkuste zvýšit počet cyklů na 10000. Co se děje nyní. Jaký je návratový kód programu? (''echo $?'')
* Nyní zkuste spustit valgrind ./prog 2>log a podívejte se do souboru ''log''. Zkuste sami rozluštit co valgrind vypisuje.
* Podobně si vyzkoušejte ''gdb'' s prikazy ''run'' a ''bt'':
gdb ./prog
(gdb) run
(gdb) bt
* Podobně zanalyzujte následující kód
#include
static int arr[5] = { 1, 2, 3, 4, 5 };
static void endless(int size)
{
for (int i = 0; i < size; ++i)
printf(" %d", arr[i]);
printf("\n");
endless(size * 10);
}
int main(int argc, char *argv[])
{
endless(1);
return 0;
}
* Zjistěte, co dělá funkce ''a()''. Nejdříve si prohlídněte kód a zkuste rozluštit, co se v něm děje. Potom si rozmyslete, jak funkci otestovat a otestujte ji.
#include
static int a(int i);
static int b(int i);
static int c(int i);
static int d(int i);
static int a(int i)
{
int shift = sizeof(int) * 8 - 1;
if ((i & (0x1 << shift)) == INT_MIN){
return i + a(0);
}else if (i % 2 == 0){
return a(i + 1) - 1;
}else{
return i + b(i) / 2;
}
}
static int b(int i)
{
if ((i & 0x1) == 0){
return d(i);
}else{
return c(i - 1);
}
}
static int c(int i)
{
int j = i / 2;
if (i == 0){
return 2;
}else{
return b(j);
}
}
static int d(int i)
{
int ip = i + 1;
int im = i - 1;
if (i == 1)
return ip;
return b(im);
}
int main(int argc, char *argv[])
{
...
}
* Zkuste teď svůj testovací program spustit pomocí ''gdb'' nebo ''cgdb''. Vyzkoušejte si následující příkazy.
run -- spustí běh
break [název funkce nebo číslo řádku] -- nastaví breakpoint
tbreak [název funkce nebo číslo řádku] -- nastaví jednorázový breakpoint
clear [název funkce nebo číslo řádku] -- smaže breakpoint
info break -- zobrazí seznam breakpointů
step -- provede jeden krok program (zkratka s)
step [počet kroků] -- provede uvedený počet kroků programu
display [výraz] -- nastaví výraz, který se vypíše při každém zastavení
backtrace -- vypíšte backtrace
frame -- zobrazí rámec
up [počet rámců] -- posune program o počet rámců zpět
down [počet rámců] -- posune program o počet rámců dopředu
info locals -- zobrazí lokální proměnné
info args -- zobrazí argumenty rámce
info variables -- zobrazí všechny statické a globální proměnné
==== Pokročilé úkoly ====
* Program rozšířte o výpisy jak v angličtině tak češtině s využítím proměnné prostředí env (viz ''lec03/main_env.c'') s nastavení LOCALE ([[https://www.ibm.com/docs/en/aix/7.1?topic=locales-understanding-locale-environment-variables|understanding-locale-environment-variables]])
* int main(int argc, char **argv, char **envp)