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