Prednáška 4
- Oznamy
- Knižnica
math - Funkcie
- Výhody funkcií
- Definícia funkcie
- Ďalšie príklady funkcií
- Príkaz
return - Lokálne a globálne premenné
- Parametre funkcií
- Programy s viacerými funkciami
- Funkcia
main - Funkcie: zhrnutie
Oznamy
Papierové rozcvičky
- Od budúceho týždňa budú na niektorých cvičeniach papierové rozcvičky.
- Je to krátky test z učiva, na ktoré už boli príklady na testovači.
- Bude povolené používať pero a ťahák vo forme jedného obojstranne popísaného listu A4.
- Zakázané sú akékoľvek elektronické zariadenia a komunikácia s inými ľuďmi okrem vyučujúcich.
Prečo robíme takéto rozcvičky a písomné testy na papieri?
- Dobré na precvičenie porozumenia hotovým programom (treba na ďalších predmetoch aj pri štúdiu odbornej literatúry).
- Lepšie sústredenie, keď vás nevyrušuje počítač a internet.
- Núti vás dobre si premyslieť každý detail, nie len náhodne skúšať meniť rôzne časti programu, kým nezačne fungovať.
- Pri zložitejších programoch sa už nedá postupovať náhodne.
- Na papieri miernejšie hodnotíme drobné chyby ako chýbajúca
bodkočiarka, to vám na počítači pomôže nájsť kompilátor.
Dôležitejšie je sa rozhodnúť, či má byť niekde napríklad
i < naleboi <= n.
Budúci týždeň
- Pondelok na cvičeniach papierová rozcvička, vzorové riešenia rozcvičky, možnosť spýtať sa k úlohám, ktoré majú termín do pondelka večera
- Papierová rozcvička sa bude týkať prednášok 1-3.
- Utorok na cvičeniach programátorská rozcvička a ďalšie programátorské úlohy s termínom ďalší pondelok večer
- Prednášky pondelok a stredu ako obvykle
Body
- Na testovači v záložke Body postupne budú pribúdať body z cvičení.
- Už tam sú body z minulého týždňa.
Knižnica math
- Obsahuje matematické funkcie, ktoré sa dajú použiť v C.
- Na používanie týchto funkcií je potrebné do hlavičky programu pridať riadok
#include <math.h> - Príklady funkcií z tejto knižnice:
cos(x),sin(x),tan(x)(tangens),acos(x)(arkus kosínus),exp(x)(ex),log(x)(prirodzený logaritmus),pow(x,y)(xy),sqrt(x)(odmocnina),abs(x)(absolútna hodnota),floor(x)(dolná celá časť), … - Pri použití funkcie treba do príkazu na kompilovanie pridať prepínač
-lm, napríklad:gcc -o program program.c -lm - Viac detailov o knižnici možno nájsť v dokumentácii.
Dĺžka úsečky
Nasledujúci program použije funkciu sqrt z knižnice math na výpočet dĺžky úsečky spájajúcej dva body zadané súradnicami [Ax,Ay] a [Bx,By]:
#include <stdio.h>
#include <math.h>
int main() {
double Ax, Ay, Bx, By;
printf("Zadaj suradnice bodu A: ");
scanf("%lf %lf", &Ax, &Ay);
printf("Zadaj suradnice bodu B: ");
scanf("%lf %lf", &Bx, &By);
double dlzka = sqrt((Ax - Bx) * (Ax - Bx) + (Ay - By) * (Ay - By));
printf("Dlzka usecky AB je: %f\n", dlzka);
}
Príklad behu
Zadaj suradnice bodu A: 0 0
Zadaj suradnice bodu B: 3 4
Dlzka usecky AB je: 5.000000
Funkcie
Funkcia je samostatný kus kódu (postupnosť príkazov) s určitým menom. Po zavolaní funkcie jej menom sa daná postupnosť príkazov vykoná.
- V iných programovacích jazykoch sa používajú aj podobné termíny ako procedúra, metóda, či podprogram.
- Funkcia vo všeobecnosti dostane niekoľko (aj nula) vstupných argumentov, ktoré môže pri svojom behu používať.
- Funkcia tiež môže vrátiť výstupnú hodnotu.
- Ide teda o veľmi podobný koncept ako funkcie v matematike (ako
napríklad sin(x)).
- Oboje si možno predstaviť ako „krabičku”, ktorá na základe niekoľkých vstupných hodnôt vráti nejakú výstupnú hodnotu.
- Funkcie v C ale môžu okrem vracania výstupných hodnôt vykonávať ľubovoľný kód, teda napríklad aj niečo vypisovať na konzolu a podobne.
Obvod trojuholníka bez použitia funkcií
Užitočnosť funkcií ilustrujeme na príklade. Chceme napísať program, ktorý od používateľa načíta súradnice vrcholov trojuholníka ABC a na výstup vypíše obvod tohto trojuholníka. Beh takéhoto programu teda môže vyzerať napríklad takto:
Zadaj suradnice vrcholu A: 0 0
Zadaj suradnice vrcholu B: 3 0
Zadaj suradnice vrcholu C: 0 4
Obvod trojuholnika ABC: 12
Obvod spočítame ako súčet dĺžok jednotlivých strán, v programe teda trikrát opakujeme výpočet dĺžky strany:
#include <stdio.h>
#include <math.h>
int main() {
double Ax, Ay, Bx, By, Cx, Cy;
printf("Zadaj suradnice vrcholu A: ");
scanf("%lf %lf", &Ax, &Ay);
printf("Zadaj suradnice vrcholu B: ");
scanf("%lf %lf", &Bx, &By);
printf("Zadaj suradnice vrcholu C: ");
scanf("%lf %lf", &Cx, &Cy);
/* Spocitaj dlzky jednotlivych stran: */
double bc = sqrt((Bx - Cx) * (Bx - Cx)
+ (By - Cy) * (By - Cy));
double ac = sqrt((Ax - Cx) * (Ax - Cx)
+ (Ay - Cy) * (Ay - Cy));
double ab = sqrt((Ax - Bx) * (Ax - Bx)
+ (Ay - By) * (Ay - By));
printf("Obvod trojuholnika ABC: %f\n", ab + ac + bc);
}
Opakované písanie toho istého vzorca na výpočet dĺžky strany je prácne; navyše pri ňom ľahko spravíme chybu. V nasledujúcom programe ho teda nahradíme funkciou.
Obvod trojuholníka s použitím funkcie
Videli sme teda, že na výpočet obvodu trojuholníka sa nám môže zísť funkcia počítajúca vzdialenosť medzi dvoma bodmi v rovine, t.j. dĺžku úsečky. Tá môže v C vyzerať napríklad takto:
double dlzka(double x1, double y1, double x2, double y2) {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
Týmto sme zadefinovali funkciu s názvom dlzka so štyrmi vstupnými
argumentmi x1, y1, x2, y2 typu double, reprezentujúcimi súradnice
dvojice bodov v rovine. Pred samotný názov funkcie sme zadali návratový
typ funkcie, ktorým je double – táto funkcia teda bude vracať na
výstupe reálne čísla. Nakoniec sme zadefinovali samotné telo funkcie,
tentokrát pozostávajúce z jediného špeciálneho príkazu return, ktorým
funkcia vracia svoju výstupnú hodnotu.
Kompletný program na výpočet obvodu trojuholníka môže s použitím funkcie
dlzka vyzerať napríklad takto:
#include <stdio.h>
#include <math.h>
/* Definicia funkcie dlzka: */
double dlzka(double x1, double y1, double x2, double y2) {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int main() {
double Ax, Ay, Bx, By, Cx, Cy;
printf("Zadaj suradnice vrcholu A: ");
scanf("%lf %lf", &Ax, &Ay);
printf("Zadaj suradnice vrcholu B: ");
scanf("%lf %lf", &Bx, &By);
printf("Zadaj suradnice vrcholu C: ");
scanf("%lf %lf", &Cx, &Cy);
// Spocitaj dlzky jednotlivych stran:
// Volanie funkcie dlzka s argumentmi Bx, By, Cx, Cy
double bc = dlzka(Bx, By, Cx, Cy);
// Volanie funkcie dlzka s argumentmi Ax, Ay, Cx, Cy
double ac = dlzka(Ax, Ay, Cx, Cy);
// Volanie funkcie dlzky s argumentmi Ax, Ay, Bx, By
double ab = dlzka(Ax, Ay, Bx, By);
printf("Obvod trojuholnika ABC: %f\n", ab + ac + bc);
}
Telo funkcie nemusí pozostávať iba z jediného príkazu. Napríklad ešte
jednoduchšie by sme funkciu dlzka mohli napísať takto:
double dlzka(double x1, double y1,
double x2, double y2) {
double dx = x1 - x2;
double dy = y1 - y2;
return sqrt(dx * dx + dy * dy);
}
Cvičenie:
- V jazyku Pascal existuje funkcia
sqr, ktorá na vstupe berie reálne čísloxa výstupom je toto číslo umocnené na druhú. Naprogramujte túto funkciu v C a použite ju na zjednodušenie funkciedlzka. - Mohli by sme použiť aj
pow(x,2)(treba ` #include <math.h>), ale môže byť jednoduchšie zrátaťx*x`.
Výhody funkcií
Funkcie sú užitočné z viacerých dôvodov:
- Umožňujú vytvoriť „skratku” pre často používané časti kódu, ktoré tak nie je nutné zakaždým písať nanovo.
- Umožňujú používanie kusov kódu vytvorených iným programátorom.
Napríklad sme použili hotovú funkciu
sqrt, ktorá vráti druhú odmocninu svojho vstupu. - Funkcie umožňujú rozdeliť písanie programu na menšie časti. Zvlášť sa môžeme sústrediť na telo programu, ktoré používa funkciu a zvlášť na implementáciu samotnej funkcie. Tieto dve časti môžu robiť aj rôzni programátori.
Definícia funkcie
Definícia funkcie pozostáva z nasledujúcich častí:
- Typ návratovej hodnoty funkcie. Funkcia z úvodného príkladu
napríklad vracia vzdialenosť bodov v rovine, teda hodnotu typu
double. Podobne môžeme písať funkcie vracajúce iné návratové typy, napríkladintalebobool. Funkcie, ktoré nemajú vracať žiadnu hodnotu majú špeciálny návratový typvoid. Ide typicky o funkcie, ktoré len vykonajú určitú činnosť, napríklad vypísanie textu na konzolu a podobne. - Identifikátor funkcie. Funkciu môžeme (v rámci určitých medzí)
pomenovať prakticky ľubovoľne, rovnako ako pri premenných. Vhodné je
však použiť názov, ktorý vystihuje úlohu danej funkcie (napríklad
dlzka). - Zoznam vstupných parametrov funkcie. V zátvorkách za názvom
funkcie je zoznam typov a identifikátorov vstupných argumentov,
ktoré funkcia očakáva. V úvodnom príklade funkcia očakáva súradnice
dvoch bodov v rovine, teda štyri hodnoty typu
double. Ak funkcia neočakáva žiaden vstupný argument, môžeme do zátvoriek napísaťvoidalebo nechať zoznam argumentov prázdny. - Telo funkcie. Do zložených zátvoriek za definíciou funkcie píšeme postupnosť príkazov, ktoré má funkcia vykonať.
- Príkaz
return. Vracia návratovú hodnotu funkcie.
typ_navratovej_hodnoty identifikator_funkcie(zoznam_vstupnych_argumentov) {
telo_funkcie // Môže obsahovať príkazy return.
}
Ďalšie príklady funkcií
Súčet čísel od a po b
Ukážeme si tri verzie funkcie, ktorá dostane dvojicu celých čísel a, b a spočíta súčet všetkých celých čísel od a po b vrátane.
- Prvá verzia tento súčet vráti ako návratovú hodnotu.
- Druhá verzia súčet vypíše, vrátane sčitovaných čísel, jej návratový
typ bude
void. - Tretia verzia súčet aj vypíše aj vráti.
/* Funkcia, ktora vrati sucet a + (a+1) + ... + (b-1) + b */
int sum(int a, int b) {
int result = 0;
for (int i = a; i <= b; i++) {
result += i;
}
return result;
}
/* Funkcia, ktora vypise cisla a, (a+1), ..., b a ich sucet */
void printNumbers(int a, int b) {
int result = 0;
for (int i = a; i <= b; i++) {
if (i > a) {
printf(" + ");
}
printf("%d", i);
result += i;
}
printf(" = %d\n", result);
}
/* Funkcia, ktora vypise cisla a, (a+1), ..., b a ich sucet
* a tento sucet aj vrati. */
int sumAndPrint(int a, int b) {
int result = 0;
for (int i = a; i <= b; i++) {
if (i > a) {
printf(" + ");
}
printf("%d", i);
result += i;
}
printf(" = %d\n", result);
return result;
}
Program využívajúci tieto funkcie môže vyzerať napríklad takto:
#include <stdio.h>
// sem pridu definicie funkcii sum, printNUmbers, sumAndPrint
// uvedene vyssie
int main() {
int a, b;
printf("Zadaj dvojicu celych cisel: ");
scanf("%d %d", &a, &b);
printf("Test funkcie sum\n");
printf("Sucet celych cisel od %d po %d: ", a, b);
// zavolame funkciu a vysledok priamo vypiseme
printf("%d\n", sum(a, b));
// vysledok funkcie mozeme ulozit do premennej
// a pouzit neskor:
int sucet = sum(a, b);
printf("Druha mocnina suctu cisel je: %d\n", sucet * sucet);
printf("\nTest funkcie printNumbers\n");
// tato funkcia nema vysledok, nic neukladame
printNumbers(a, b);
printf("\nTest funkcie sumAndPrint\n");
// dalsia funkcia vypise aj vrati hodnotu
int sucet2 = sumAndPrint(a, b);
printf("Druha mocnina suctu cisel je: %d\n", sucet2 * sucet2);
// vystupnu hodnotu funkcie mozeme aj odignorovat
sumAndPrint(a,b);
}
Príkaz return
Pozrime sa teraz bližšie na špeciálny príkaz return:
- Po vykonaní príkazu
returnje funkcia okamžite zastavená a jej výstupná hodnota je výraz za slovomreturn. - Funkcia môže obsahovať aj viacero volaní
return. Akonáhle sa však jedno z nich vykoná, funkcia končí s danou návratovou hodnotou. Napríklad:
#include <stdio.h>
int f(void) {
return 1;
return 2; // Nikdy sa nevykona.
return 3; // Nikdy sa nevykona.
}
int main() {
printf("%d\n", f()); // Vypise 1.
}
- Funkcia s návratovým typom
voidmôže tiež obsahovať príkazreturn, avšak bez návratovej hodnoty.- V takom prípade
returnslúži iba na ukončenie vykonávania funkcie, väčšinou je lepšie prepísať inak - Tu je ukážka, kde namiesto
returnby bolo lepšie použiťelse:
- V takom prípade
#include <stdio.h>
void akeCislo(int n) {
if (n >= 0) {
printf("Cislo je nezaporne.\n");
return; //lepie by bolo pouzit else namiesto return
}
// Vykona sa len v pripade n < 0:
printf("Cislo je zaporne.\n");
}
int main() {
int n;
scanf("%d", &n) ;
akeCislo(n);
}
Napríklad minimum z dvoch čísel môžeme vypočítať dvoma rôznymi spôsobmi:
int minimum1(int a, int b) {
int minval;
if (a < b) {
minval = a;
} else {
minval = b;
}
return minval;
}
int minimum2(int a, int b) {
if (a < b) {
return a;
} else {
return b;
}
}
- Funkcie s návratovým typom iným ako
voidje žiadúce písať tak, aby na ľubovoľnom vstupe ich vykonávanie vždy skončilo príkazomreturn.- Ak totiž takáto funkcia skončí inak, než príkazom
return, jej výstupná hodnota je nedefinovaná (použije sa „hocijaký nezmysel”), čo môže viesť k zákerným chybám.
- Ak totiž takáto funkcia skončí inak, než príkazom
Cvičenie: Nájdite hodnotu nasledujúcej funkcie pre n=6 a n=7. Viete stručne popísať, čo funkcia robí pre všeobecné n?
int zahada(int n) {
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
return i;
}
}
return n;
}
Lokálne a globálne premenné
Premenné v C možno rozdeliť na globálne a lokálne:
- Globálne premenné možno používať vo všetkých funkciách, ktoré sú v programe definované za touto premennou.
- Lokálne premenné sú definované vo vnútri funkcie a môžu sa používať iba v rámci nej (alebo iba v rámci niektorej časti funkcie, ak je premenná definovaná napríklad v tele cyklu a podobne).
Platí navyše, že:
- Viaceré funkcie môžu mať lokálne premenné s tým istým názvom – každá funkcia potom používa tú svoju. Toto sa bežne používa.
- Ak má lokálna premenná rovnaký názov ako nejaká globálna premenná, lokálna premenná prekryje globálnu – funkcia teda používa svoju lokálnu premennú (bližšia košeľa ako kabát). Toto radšej nerobte, môže vzniknúť chaos.
Je silno odporúčané používať predovšetkým lokálne premenné. Väčšie programy s globálnymi premennými môžu byť veľmi neprehľadné.
Nasledujúci program obsahuje globálnu premennú x a v každej funkcii
lokálnu premennú s názvom y:
#include <stdio.h>
int x;
void f(void) {
int y = 10;
printf("%d %d\n", x, y);
}
int main() {
x = 10;
int y = 20;
f(); // Vypise 10 10.
printf("%d %d\n", x, y); // Vypise 10 20.
}
Parametre funkcií
Vstupné parametre funkcií sa správajú ako lokálne premenné danej funkcie. Pri volaní funkcie sa každému parametru priradí určitá hodnota. Uvažujme napríklad funkciu
void f(int a, int b) {
// ...
}
Pri volaní f(1,2) sa parametru a priradí hodnota 1 a parametru b sa priradí hodnota 2.
Tieto sa ďalej správajú ako lokálne premenné funkcie f. Možno ich teda
meniť, ale táto zmena sa neprejaví na mieste, odkiaľ funkciu voláme.
Príklad:
#include <stdio.h>
void f(int n) {
n++;
printf("%d\n", n);
}
int main() {
int n = 1;
f(n); // Vypise 2
printf("%d\n", n); // Vypise 1
}
Programy s viacerými funkciami
V programe možno volať iba funkcie, ktoré už predtým boli niekde definované.
Tento program typicky neskompiluje (vo vnútri f1 ešte nepozná f2)
#include <stdio.h>
void f1() {
f2();
}
void f2() {
printf("Hello, world!\n");
}
int main() {
f1();
}
Tento program je v poriadku:
#include <stdio.h>
void f2() {
printf("Hello, world!\n");
}
void f1() {
f2();
}
int main() {
f1();
}
Funkcia main
Špeciálnou funkciou v C je funkcia main.
- Jej volanie je automatické (začína sa ním beh programu).
- Výstupná hodnota funkcie
mainsa interpretuje ako „spôsob ukončenia” programu (napríklad 0 pre korektné ukončenie, iné hodnoty pre chyby).- Funkciu
mainmôžeme teda ukončit napríklad príkazomreturn 0. - Náš testovač vyžaduje, aby sa program ukončil s návratovou hodnotou 0, inak predpokladá, že došlo k chybe počas behu programu (napr. deleniu nulou).
- Funkciu
- Funkcia
mainmôže mať aj určité presne špecifikované parametre (o nich neskôr).
Funkcie: zhrnutie
- Funkcie nám umožňujú rozbiť väčší program na menšie logické časti a tým ho sprehľadniť. Tiež nám umožňujú vyhnúť sa opakovaniu podobných kusov kódu.
- Hlavička funkcie obsahuje návratový typ (môže byť
void), meno funkcie, typy a mená parametrov. - V tele funkcie sú samotné príkazy. Vypočítanú hodnotu vrátime
príkazom
return. - Lokálne premenné sú viditeľné len vo funkcii, ktorá ich definuje, globálne vo všetkých funkciách.
- Parametre sú lokálne premenné inicializované hodnotami, ktoré sa zadajú pri volaní funkcie.