===== 7. Paměť programu, standardní knihovny C. Rekurze =====
* prezentace: {{courses:b0b36prp:lectures:b0b36prp-lec07-slides.pdf|}}
* zkrácená verze: {{courses:b0b36prp:lectures:b0b36prp-lec07-handout.pdf|}}
* zkrácená verze 2x2: {{courses:b0b36prp:lectures:b0b36prp-lec07-handout-2x2.pdf|}}
* zkrácená verze 3x3: {{courses:b0b36prp:lectures:b0b36prp-lec07-handout-3x3.pdf|}}
* přiložené demonstrační programy: {{courses:b0b36prp:lectures:b0b36prp-lec07-codes.zip|}}
--- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2025/09/19 19:45//
--- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2023/11/16 15:32// / **Aktualizace**: Doplnění volání ''fill_numbers()'' a ''print()'' v ''save_struct.c''.
--- //[[faiglj@fel.cvut.cz|Jan Faigl]] 2023/11/19 20:00//
** Umístění errno.h **
Chybové kódy standardní knihovny C jsou definovány v souboru ''errno.h'', umístění souboru s definicí chyb je však na různých systémch různé. V unixovových systém se jedná zpravidla o ''/usr/include/errno.h'' případně ''/usr/include/sys/errno.h'', popis jednotlivých konstant a hodnot lze také najít např. v ''man errno''. V distribucích Linuxu je situace trošku složitější, neboť umístění záleží na konkrétní distribuci, např.
lP5oGIqdWEU
* /usr/include/asm/errno.h
* /usr/include/asm-generic/errno-base.h
* /usr/include/asm-generic/errno.h
* /usr/lib/bcc/include/linux/errno.h
* /usr/include/sys/errno.h
* /usr/include/asm-i386/errno.h
Konkrétní soubor můžeme najít například příkazem
grep -R 17 /usr/include/**/errno*.h
/usr/include/asm-generic/errno-base.h:#define EEXIST 17 /* File exists */
/usr/include/asm-generic/errno.h:#define EUCLEAN 117 /* Structure needs cleaning *
jehož výstup nám napoví, že na tomto konkrétním Linuxu jsou rozděleny chybové kódy do souborů ''errno-base.h'' (obsahující základní kódy) a ''errno.h'' obsahující rozšířené kódy, tj. kódy s vyšší hodnotou.
Dále je většinou v Linuxu standardní knihovna libc realizována jako ''GNU libc'' ([[ https://www.gnu.org/software/libc/|glibc ]]), což přináší některé [[https://www.gnu.org/software/libc/manual/ | zajímavé funkce ]], které na druhou stranu nejsou dostupné na jiných OS, nelinuxového typu, např. OS X.
Q: Je nějaké omezení na hloubku rekurzivního volání?
Ano, při volání funkce se minimálně ukládá na zásobník adresa odkud funkci voláme, aby mohlo dojít k návratu z volání funkce. Dále se při každém volání mohou vytvářet na zásobníku lokální proměnné. Tedy omezení je dáno velikostí zásobníku. Omezení si můžeme vyzkoušet následujícím programem.
#include
void printValue(int v);
int main(void)
{
printValue(1);
}
void printValue(int v)
{
printf("value: %i\n", v);
printValue(v + 1);
}
a spuštěním s omezenou velikostí zásobníku
ulimit -s 1000; ./a.out | tail -n 3
value: 31730
value: 31731
Segmentation fault
a s větší velikostí.
ulimit -s 10000; ./a.out
value: 319816
value: 319817
Segmentation fault
V obou případech končí volání chybou. Ve druhém případě došlo k hlubšímu zanoření. Zotavení z chybějícího místa na zásobníku je velmi náročné, narozdíl od dynamické alokace. Výchozí hodnota zásobníku je nastavana na relativně dostačující velikost pro běžné programy. Nicméně v případě rekurzivního řešení je dobré pamatovat, že místo na zásobníku není neomezené. Proto je vždy vhodné rozumět úloze a konkrétním (typickým) vstupům a prostředí. Například na základě velikosti vstupu můžeme odhadnout, potřebnou velikost zásobníku a případně uživatele informovat. Asi nejhorší případ je, když program padá a netušíme proč tomu tak je.