Prostudujte si přednášku z jazyka C na stránce http://efis.tul.cz/~dana.nejedlova/ v odstavci "Programování I a II" od smínku "Pointery" do snímku "Funkce jako parametry funkcí". Vyzkoušejte si všechny programy na snímcích, které si máte prostudovat, a i programy, které jsou níže v tomto souboru. Při pouštění programů si mimo jiné zkuste přehodit pořadí funkcí, které v nich jsou, viz snímky mých přednášek z jazyka C, které jste si měli prostudovat minulý týden, "Umístění definice funkcí" a "Funkční prototyp". První program je kalkulačkou, ve které se z menu vybírá pomocí celých čísel. Pokud uživatel zadá volbu mimo rozsah menu (0, 1, 2), tak se spustí default větev programu. Default větev je nejjednodušší a preferovaný způsob, jak ošetřit interval zadávaných hodnot pro výběr z menu. #include #define KONEC 0 double precti_cislo(void) { double cislo; printf("Zadej cislo: "); while (scanf("%lf", &cislo) != 1) { printf("Zadali jste spatne.\n"); while (getchar() != '\n') ; printf("Zadej cislo: "); } while (getchar() != '\n') ; return cislo; } int precti_cele_cislo(void) { int cislo; printf("Zadej cele cislo: "); while (scanf("%d", &cislo) != 1) { printf("Zadali jste spatne.\n"); while (getchar() != '\n') ; printf("Zadej cele cislo: "); } while (getchar() != '\n') ; return cislo; } int main(void) { int volba; double cislo1, cislo2; do { printf("Zadejte\n"); printf("%d pro ukonceni programu,\n", KONEC); printf("1 pro scitani,\n"); printf("2 pro nasobeni.\n"); volba = precti_cele_cislo(); switch (volba) { case KONEC: printf("Konec programu."); break; case 1: printf("Scitani\n"); cislo1 = precti_cislo(); cislo2 = precti_cislo(); printf("%f + %f = %f\n", cislo1, cislo2, cislo1 + cislo2); break; case 2: printf("Nasobeni\n"); cislo1 = precti_cislo(); cislo2 = precti_cislo(); printf("%f * %f = %f\n", cislo1, cislo2, cislo1 * cislo2); break; default: printf("Neznama volba.\n"); break; } } while (volba != KONEC); return 0; } Druhý program je kalkulačka s jedinou funkcí. Je zde nejlepší řešení, jak se ověří, že bylo zadáno celé číslo v určitém intervalu. Na rozdíl od přechozího příkladu tím číslem není volba z menu, a proto nemůžeme použít řídící strukturu switch, protože switch v jazyce C netestuje intervaly ale jen rovnost s datovým typem int nebo char. Ověření čísla je dekomponováno na funkci pro zadání jakéhokoliv celého čísla a funkci pro zadání celého čísla v intervalu, která ji volá. Potom můžeme funkci pro zadání jakéhokoliv celého čísla použít i na jiných místech programu, kde bychom chtěli zadat libovolné celé číslo, což zvyšuje znovupoužitelnost kódu, viz https://it-slovnik.cz/pojem/znovupouzitelnost #include #define KONEC 'k' #define MIN 0 #define MAX 12 int precti_znak_a_vyprazdni_buffer(void) { // V materialu pro 10. vyukovy tyden v adresari \kalkulacka\tradicni_reseni je podobna funkce vracejici datovy typ char, ale int je lepsi, protoze funkce getchar() vraci int. int znak; printf("Zadejte znak: "); znak = getchar(); while (getchar() != '\n') ; return znak; } int precti_cele_cislo(void) { int cislo; while (scanf("%d", &cislo) != 1) { printf("Zadali jste spatne.\n"); while (getchar() != '\n') ; printf("Zadej cele cislo: "); } while (getchar() != '\n') ; return cislo; } int zadej_cislo_v_intervalu(int min, int max) { int cislo; printf("Zadej cele cislo v intervalu od %d do %d: ", min, max); while ((cislo = precti_cele_cislo()) < min || cislo > max) { printf("Zadali jste cislo mimo intreval.\n"); printf("Zadej cele cislo v intervalu od %d do %d: ", min, max); } return cislo; } int faktorial(int x) { // Viz snimek mych prednasek z jazyka C "Operator carky". Na snimku "Rekurzivni funkce" je jine reseni. int i, vysledek; for (i = 1, vysledek = 1; i <= x; i++) vysledek *= i; return vysledek; } int faktorial_alternativni(int x) { int i, vysledek = 1; /* vysledek inicializujeme hodnotu faktorialu pro argument 0. */ for (i = x; i; i--) /* i s hodnotou 0 je false a ukonci tedy cyklus. */ vysledek *= i; /* Provede se jen pro argument x > 0, jinak zustava vysledek = 1. */ return vysledek; } int main(void) { int volba; int cislo; do { printf("Zadejte znak '%c' pro ukonceni programu.\n", KONEC); printf("Jakykoli jiny znak umozni spocitat faktorial cisla.\n"); volba = precti_znak_a_vyprazdni_buffer(); switch (volba) { case KONEC: printf("Konec programu."); break; default: printf("Funkce faktorial\n"); cislo = zadej_cislo_v_intervalu(MIN, MAX); printf("Faktorial cisla %d je %d.\n", cislo, faktorial(cislo)); break; } } while (volba != KONEC); return 0; } Třetí program je příkladem nesprávného řešení funkce pro zadávání čísla. Funkce precti_cele_cislo() má místo while cyklu rekurzi, což znamená, že volá sama sebe. Funkce, která volá sama sebe, zabírá v části operační paměti zvané zásobník (viz snimek mých přednásek z jazyka C "Dynamická alokace paměti") stále více místa, až nakonec může zásobník přetéct a program havaruje. Program s takovou funkcí zabírá stále více paměti, protože funkce zůstává v paměti, dokud nejsou ukončeny všechny funkce, které jsou z ní volány, a funkce volané z volaných funkcí. Takže v tomto případě poté, co uživatel dvakrát zadá něco jiného než číslo, tak funkce precti_cele_cislo() bude v paměti ve třech exemplářích a ty se z paměti odstraní až po zadání správného čísla nebo po havárii programu typu přetečení zásobníku, viz https://en.wikipedia.org/wiki/Stack_overflow#Very_deep_recursion Funkce v tomto programu má místo proměnné pro jediné číslo pole pro 100000 čísel, protože potom lze pozorovat havárii programu už po 5 chybných zadáních čísla. Po spuštění programu je možné ve Správci úloh (Task manager) operačního systému Windows pozorovat, jak po každém chybném vstupu zabírá program více paměti. #include #define KONEC 0 int precti_cele_cislo(void) { int cislo[100000]; printf("Zadej cele cislo: "); if (scanf("%d", &cislo[0]) != 1) { printf("Zadali jste spatne.\n"); while (getchar() != '\n') ; return precti_cele_cislo(); } while (getchar() != '\n') ; return cislo[0]; } int main(void) { int cislo; do { printf("Zadejte nejake cele cislo.\n"); printf("Cislo %d ukonci program.\n", KONEC); cislo = precti_cele_cislo(); printf("Zadali jste cislo %d.\n", cislo); } while (cislo != KONEC); return 0; } Čtvrtý program je alternativním správným řešením zadávání čísla v intervalu, ve kterém je argument do funkce precti_cele_cislo() předán odkazem, viz část mých přednášek o pointerech, kterou si máte tento týden prostudovat. #include #define KONEC 0 #define MIN 0 #define MAX 12 void precti_cele_cislo(int *cislo) { while (scanf("%d", cislo) != 1) { printf("Zadali jste spatne.\n"); while (getchar() != '\n') ; printf("Zadej cele cislo: "); } while (getchar() != '\n') ; } int zadej_cislo_v_intervalu(int min, int max) { int cislo; printf("Zadej cele cislo v intervalu od %d do %d: ", min, max); while (precti_cele_cislo(&cislo), cislo < min || cislo > max) { /* Operator carky */ printf("Zadali jste cislo mimo interval.\n"); printf("Zadej cele cislo v intervalu od %d do %d: ", min, max); } return cislo; } int main(void) { int cislo; do { printf("Zadejte nejake cele cislo.\n"); printf("Cislo %d ukonci program.\n", KONEC); cislo = zadej_cislo_v_intervalu(MIN, MAX); printf("Zadali jste cislo %d.\n", cislo); } while (cislo != KONEC); return 0; } Pátý program ukazuje použití pointeru na funkci, viz snímky mých přednášek z jazyka C "Pointer na funkci" a "Funkce jako parametry funkcí". #include int precti_cele_cislo(void) { int cislo; printf("Zadej cele cislo: "); while (scanf("%d", &cislo) != 1) { printf("Zadali jste spatne.\n"); while (getchar() != '\n') ; printf("Zadej cele cislo: "); } while (getchar() != '\n') ; return cislo; } int vrat_nejmensi(int x, int y, int z) { // Vnoreny ternarni operator, viz snimek mych prednasek z jazyka C "Podmineny vyraz – ternarni operator". return (x > y) ? (y > z) ? z : y : (x > z) ? z : x; } int vrat_nejvetsi(int x, int y, int z) { return (x < y) ? (y < z) ? z : y : (x < z) ? z : x; } int krajni_cislo(int x, int y, int z, int (*p_f)()) { return p_f(x, y, z); } int main(void) { int cislo1, cislo2, cislo3; cislo1 = precti_cele_cislo(); cislo2 = precti_cele_cislo(); cislo3 = precti_cele_cislo(); printf("Nejmensi cislo je %d.\n", krajni_cislo(cislo1, cislo2, cislo3, vrat_nejmensi)); printf("Nejvetsi cislo je %d.\n", krajni_cislo(cislo1, cislo2, cislo3, vrat_nejvetsi)); return 0; }