Table of Contents

6 - Dynamická alokace a textové soubory

Cíle cvičení

  1. Dynamická alokace: malloc a realloc.
  2. Načítání vstupu ze souboru.
  3. Zpracování argumentů programu.
  4. Implementace funkčního a správného programu s ošetřením možných chybových stavů.
Cvičení na dynamickou alokaci pokračuje 7 - Dynamická alokace a struktury. Věnujte dostatek času pochopení základů a případně pokračujete na dalším cvičení.

Materiály

Úkoly

Funkčnost programu si můžete ověřit vytvořením kontrolního výstupu programem tac a porovnáním s výstupem implementovaného programu. Např. ./main in.txt > my_out.txt; tac in.txt > out.txt; diff my_out.txt out.txt

Program dekomponujeme na následující funkce.

Implementační postup můžeme zvolit například následující.

  1. Implementace načtení řádku znak-po-znaku s dynamickou alokací, pro zjednodušení budeme uvažovat FILE *fd = stdin;.
  2. Implementace načtení řádků.
  3. Vytištění výstupu.
  4. Přidání zpracování argumentu programu se jménem vstupního souboru.
  5. Rozlišení chyby vstupu a chyby alokace.

Načtení řádku s dynamickou alokací

Příklad funkce print()

Příklad volání načtení vstupu a tisk

Načtení řádků do pole ukazatelů na textové řetězce

Příklad volání read_lines(), print_lines() a free_lines()

Zpracování argumentu programu se jménem vstupního programu

Příklad zpracování argumentu programu

Přidání ošetření chybových stavů a reportování chyby návratovou hodnotou

Příklad chybových kódu programu

Defince prototypů funkcí

Příklad hlavičky funkcí print a read

Příklad funkce print_lines

Jiný příklad tisku jednoho řádku

Kratší verze ternárním operátorem ? :

Testování vstupu a chybových stavů

limits  -v 2621440 ./main <in-long.txt; echo $?
ld-elf.so.1: /lib/libc.so.7: mmap of entire address space failed: Cannot allocate memory
1

limits -v 5242880 ./main <in-long.txt; echo $?
101

V Linuxu spíše použijete příkaz ulimit, který nastavuje aktuální sezení příkazového interpretu, což samo o sobě vyžaduje paměti více. Mimoto je nastavení paměti v kB a simulace malé paměti tak může být náročnější. Nicméně princip ošetření zůstává stále validní, ikdyž zrovna teď máme paměti dost, zásadní je princip, že uvažujeme takovou možnost a jsme na ni připraveni. Alternativně můžeme přijmout fakt, že chyba se může stát, ale když se stane, tak s tím nic neuděláme, což je typicky příklad vypisování zpráv na stderr, případně stdout, kde běžně nekontrolujeme, že se vypsalo vše co mělo. V takovém případě indikaci špatného chování máme, v případě přístupu do nepřidělené paměti může program stále běžet, ale nebude mít definované chování a uživatel o tom nebude informován.

int main(int argc, char const *argv[])
{
  ...
  print_error(ret);
  return ret;
}

Například

V případě jediného místa ukončení programu, return v hlavní funkci main, je to relativně snadné. Samotné rozšíření programu o testování chybových stavů je spíše pracné, než náročné. Zdrojový kód se stane relativně méně přehledný. Nicméně při krátkých funkcí je rozšíření relativně rychlé a pokud jste implementovali vlastní řešení vhodně, mělo by se pohybovat mezi 5 až 10 minutami.

limits -v 5242880 ./main <in-long.txt; echo $?
101

limits -v 12582912 ./main <in-long.txt; echo $?
...
DEBUG: n: 5921 size: 8192
DEBUG: n: 5922 size: 8192
101

head -n 5922 in-long.txt |wc
  5922   95927  742533

Omezení paměti v Linuxu

Protože v Linuxu můžeme ještě narazit na chování OS a volání malloc, které spíše vrátí přidělenou paměť (z virtuálního adreseního prostoru, který je v případě 64-bit dostatečně veliký) nicméně k fyzickému přidělení paměti dojde až při zápisu do paměti, které v případě nedostatku paměti skončí ukončením programu. Toto tzv. overcommit chování je možné nastavit globálně sysctl vm.overcommit_memory = 2 a následně vm.overcommit_ratio a vm.overcommit_kbytes, což však může mít katastrofální důsledky pro běžící OS.
Přidání uvedeného ladícího výstupu je příkladem ladění, které není tak přímočaré realizovat krokováním, neboť nás zajímá poslední hodnota, kdy program selže a krokování je v takovém případě neefektivní.

Ověření chování při chybě vstupu nebo výstupu

Další úkoly k procvičení