Search
Při rekurzivním řešení výpočetního problému volá funkce seba sama, proto je důležité definovat ukončovací podmínku, kdy k volání už nedojde a naopak se vracíme z volaných funkcí. Typicky má funkce nějaký argument, který tak v podstatě hraje roli řídicí proměnné, podobně jako v cyklu.
Např.
1234 -> 10 4135 -> 13 1516 -> 13 1361 -> 11 39831 -> 24
Nápověda
int sum_of_digits(int n); int main(void) { int vals[] = { 1234, 4135, 1516, 1361, 39831 }; for (int i = 0; i < 5; ++i) { printf("%d -> %d\n", vals[i], sum_of_digits(vals[i])); } return 0; } int sum_of_digits(int n) { if (n == 0) { return 0; } else { return // digit sečteme s výsledkem volání sum_of_digits pro další digit } }
1234 -> 4321 hello -> olleh a b c -> c b a PRP -> PRP I like PRP -> PRP ekil I
#include <stdio.h> void print_reverse(char *str); int main(void) { char *strings[] = { "1234", "hello", "a b c", "PRP", "I like PRP", NULL}; char **cur = strings; while (*cur) { printf("%s -> ", *cur); print_reversed(*cur); cur += 1; putchar('\n'); } return 0; } void print_reversed(char *str) {}
Is palindrom "1234" - no Is palindrom "-121" - no Is palindrom "121" - yes Is palindrom "abba" - yes Is palindrom "abca" - no Is palindrom "A man, a plan, a canal: Panama!" - no
#include <stdio.h> #include <string.h> _Bool is_palindrom(const char *str); int main(void) { char *strings[] = { "1234", "-121", "121", "abba", "abca", "A man, a plan, a canal: Panama!", NULL }; char **cur = strings; while (*cur) { printf("Is palindrom \"%s\" - %s\n", *cur, is_palindrom(*cur) ? "yes" : "no"); cur += 1; } return 0; } static _Bool isPalindrom(const char *str, int start, int end) { .... } _Bool is_palindrom(const char *str) { return isPalindrom(str, 0, strlen(str) - 1); }
is_alpha()
ctype.h
Permute "12" 12 21 Permute "abc" abc acb bac bca cba cab
#include <stdio.h> #include <stdlib.h> #include <string.h> void permute(char *str); int main(void) { char strings[][4] = { "12", "abc", "\0"}; //2D pole znaků, proto "\0". for (char (*cur)[4] = (char (*)[4])strings; cur[0][0]; cur += 1) { permute(*cur); } return 0; } static void swap(char *a, char *b) { char t = *b; *b = *a; *a = t; } static void print_permutation(char *str, int l, int r) { ... } void permute(char *str) { printf("Permute \"%s\"\n", str); print_permutation(str, 0, strlen(str) - 1); putchar('\n'); }
char *strings[] = { "12", "abc", "\0"};
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)
revert()
getchar()
EOF
putchar()
Zasobnik ma k dispozici relativne malo pameti. Staci zjistit jaky je jeji limit a ten prekrocit $ ulimit -s 8192 $ tr -dc A-Za-z < /dev/urandom | head -c 20000000 > file_big $ tr -dc A-Za-z < /dev/urandom | head -c 5 > file_small $ cat file_small; echo QzaFT $ ./main < file_small; echo TFazQ $ ./main < file_big; echo Segmentation fault (core dumped)
revert_h()
char* read_input(size_t *index, size_t *size) { *size = 10; *index = 0; char *chars = (char *)malloc(sizeof(char)* *size); if (chars == NULL) { printf("Memory allocation failed\n"); return NULL; } char buff; while ((buff = getchar()) != EOF) { chars[*index] = buff; (*index)++; if (*index == *size) { *size *= 2; char* chars_ = (char *)realloc(chars, sizeof(char)* *size); if (chars_ == NULL) { free(chars); printf("Memory reallocation failed\n"); return NULL; } chars = chars_; } } return chars; }
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 main.c -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
11, 12, 17, 21, 22, 27, 71, 72, 77