Search
Nápověda...
Funkce main nechť načte uživatelův vstup n a zavolá funkci my_print(n). Funkce void my_print (int n) vytiskne “Hello world\n” a pokud je předaná hodnota n je větší než 1, zavolá sebe sama s n-1.
main
n
my_print(n)
void my_print (int n)
1
n-1
strlen()
Funkce main načte zadaný text a předá ho funkci my_strlen(n,0). Funkce int my_strlen (char* s, int l) se podívá na l-tý znak v s. Pokud se jedná o \0, vrátí l. Jinak vrátí výsledek volání sebe sama s l+1. Případně oddělte první volání jako my_strlen(char*) a další volání jako my_strlen_body(char*, int)
my_strlen(n,0)
int my_strlen (char* s, int l)
l
s
\0
l+1
my_strlen(char*)
my_strlen_body(char*, int)
Protože cvičíme rekurzi, odpustíme si složité načítání a alokování paměti a využijeme rekurzivního volání funkce, která bude rekurzivně načítat jeden znak getchar() a následně vytiskne načtený znak. Volání končí pokud dojde k chybě nebo je načten konec řádku \0.
getchar()
Poznámka...
Přetečení zásobníku znamená, že při definici lokálních proměnných nebo volání funkcí dojde prostor pro uložení hodnot na zásobníku a přistupovali bychom mimo přidělený paměťový prostor. To typicky způsobí chybu Segmetation fault bez dalšího upozornění, stejně jako při nesprávné práci s dynamicky alokovanou pamětí. Pro ladění programu můžeme použít alternativní správce paměti nebo programu valgrind, který umí vysvětlit situaci detailněji…
valgrind
ulimit
Řešení...
ulimit -a zobrazí aktuální nastavení. ulimit -s <velikost> nastaví velikost stacku nově spuštěných programů.
ulimit -a
ulimit -s <velikost>
11, 12, 17, 21, 22, 27, 71, 72, 77
Výpočet Fibonacciho posloupnosti je již jednou známý a dále neměnný. Nemá smysl jej pořád implementovat odznovu, proto jej chceme zařadit do knihovny, kterou budeme přepoužívat. Chceme, aby nám knihovna uměla vypočítat $n$-tý člen posloupnosti. Deklaraci (rozhraní) této funkcionality napíšeme do hlavičkového souboru fib.h:
fib.h
int fib (int i);
Implementaci (definici) pak napíšeme do zdrojového souboru fib.c:
fib.c
#include "fib.h" int fib (int i){ ... // TODO: Implement me :-) }
Knihovnu pak můžeme použít v našem programu main.c, který spočítá třicáté Fibonacciho číslo:
main.c
int main (){ return fib(30); }
Nyní vše stačí zkompilovat. Knihovnu zkompilujeme s přepínačem -c, což vytvoří nespustitelný “object file” fib.o. Druhý příkaz zkompiluje soubor main.c a načte již hotový fib.o a “spojí” je dohromady do spustitelného souboru main.
-c
fib.o
gcc -c -o fib.o fib.c gcc -o main main.c fib.o ./main echo $?
fibit.c
fibrec.c
-fPIC -shared
gcc -shared -fPIC -o libfibit.so fibit.c gcc -shared -fPIC -o libfibrec.so fibrec.c
libfib.so
cp libfibrec.so libfib.so
gcc -o main -Wl,-rpath=. -L. -lfib
ldd main
time seq 40 | main > fib_recursion.txt
cp libfibit.so libfib.so && time seq 40 | main > fib_interation.txt
readelf
Knihovnu lze samozřejmě používat v mnoha překladových jednotkách našeho projektu. Například funkce pro práci s textovými řetězci se budou používat často a na mnoha místech. Programátorům stačí znát deklaraci knihovních funkcí v hlavičkovém souboru. Problém může nastat, pokud se deklarace z hlavičkového souboru během preprocessingu “vloží” do zdrojového kódu na více míst. Demonstrujte:
my_strlen
my_strlen.c
my_strlen.h
int my_strlen(char*);
#include “my_strlen.h”
#ifndef HEADER_GUARD_MY_STRLEN_H #define HEADER_GUARD_MY_STRLEN_H 1 int my_strlen(char*); #endif