Lab 03 - Dekompozice programu

  • Pro vyučující: 03.

Výchozí soubory prg-lab03.zip (Pokročilé úkoly)

Cíle cvičení

  1. Podmínky a cykly (řídicí struktury), operátory.
  2. Implementace funkčního a správného programu s ošetřením možných chybových stavů.
  3. Dekompozice programu na funkce.
  4. 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.

  1. 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');
  2. Testujte chování programu pro špatný vstup, např. clang main.c && echo “1” | ./a.out ; echo $?.
  3. 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).
  4. 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”
  5. 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í

  1. Dekomponujte tisk trojúhelníku na funkce
    • void print_triangle(char c, int lines); a
    • void print_line(char c, int n);
  2. Zjednodušte hlavní funkci definováním funkcí
    • int read_input(int *n); a
    • int print_error(int ret, int n);
  3. 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 <input > out.std 2> out.err.
  4. Program rozšiřte o zpracování argumentů programu argc a argv s využitím funkce sscanf().

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 <stdio.h>
 
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 <stdio.h>
 
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 <limits.h>
 
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 (understanding-locale-environment-variables)
  • int main(int argc, char **argv, char **envp)
courses/b3b36prg/labs/lab03.txt · Last modified: 2026/03/02 10:51 by szadkrud