Týden 9
Viditelnost identifikátorů
Identifikátory jsou v program v jazyce C viditelné podle umístění, kde je definujeme. Identifikátor může být vidět ve všech funkcích programu nebo jen v některé funkci nebo jen v některém bloku programu. Podle viditelnosti jsou identifikátory:
- globální - jsou vidět v celém programu
- lokální - jsou vidět jen v některé části (funkce, blok)
Příklad je zde:
// globalni promenna - identifikator x int x = 5; // globalni identifikator main int main(void) { // lokalni promenna - identifikator a int a; return 0; }
Funkce II
Zápis funkce můžeme rozdělit na dvě části:
- hlavička funkce
- tělo funkce
Hlavička funkce většinou začíná návratovým typem a končí kulatou závorkou, která uzavírá argumenty funkce. Tělo funkce se pak zapíše mezi složené závorky za hlavičku funkce a obsahuje samotný program funkce.
Zde je příklad:
// hlavicka funkce void sayHello(char name[]) // telo funkce { printf("Hello %s!", name); }
Zatím jsme zapisovali funkce tak, že jsme je celé (hlavičku i tělo) zapsali před funkci main, čímž jsme umožnili viditelnosti jména funkce v těle funkce main. Zápisu hlavičky i těla funkce se říká definice funkce.
V jazyce C je možné funkce definovat až za tělem metody main. V takovém případě je nutné funkci definovat před tělem funkce main, tak abychom ji mohli v main použít. K deklaraci funkce stačí zapsat její hlavičku a ukončit ji středníkem.
#include // deklarace funkce - hlavicka ukoncena strednikem void sayHello(char[]); // funkce main - hlavni telo programu int main(void) { sayHello("Felix"); return 0; } // definice funkce - hlavicka nasledovana telem void sayHello(char name[]) { printf("Hello %s!", name); }
Předávání polí funkcím
Funkcím lze předávat i pole - jednorozměrná i vícerozměrná. Při předávání polí nemusí kompilátor znát první rozměr pole. Chceme-li předat pole jednorozměrné, může být argument typu pole bez rozměru - má prázdné hranaté závorky (viz výše). Pokud bychom chtěli předat dvourozměrné pole, musíme uvést druhý rozměr pole, pokud třírozměrné pak druhý a třetí atd.
Jazyk C ve standardu C99 umožňuje, aby funkce měly jako argumenty pole jejichž délka je zapsána v nějakém předcházejícím argumentu. Zvlášť u vícerozměrných polí se nám toto hodí v případě, kdy nechceme aby rozměry pole, které musíme kompilátoru zadat, byly konstantní. Několik příkladů:
// predavame pole ale i promenlivy pocet prvku v poli, ktery by funkce jinak neznala double sum(double numbers[], int count) { double s = 0; for (int i = 0; i < count; i++) { s = s + numbers[i]; } } // predavame dvourozmerne pole, musime zadat druhy rozmer! bool isRegular(double matrix[][10], int rows, int columns) { // telo funkce... }
Výše uvedený zápis umožňuje předávat funkci isRegular jen taková pole, která mají druhý rozměr 10. Mohli bychom tedy předat pole (reprezentující matice) o rozměrech 3x10, 1x10 nebo třeba 15x10. Nemůžeme ale funkci isRegular předat např. pole o rozměrech 2x2 nebo 3x3, což její použití velmi omezuje. Toto omezení lze v C99 řešit následujícím zápisem:
// predavame dvourozmerne pole, zadame rozmer/y promennymi bool isRegular(int rows, int columns, double matrix[][columns]) { // telo funkce... }
nebo pokud chceme uvést i rozměr, který můžeme vynechat pak takto:
// predavame dvourozmerne pole, zadame rozmer/y promennymi bool isRegular(int rows, int columns, double matrix[rows][columns]) { // telo funkce... }
Návrat hodnot z funkce
Nejčastěji vrací funkce hodnotu pomocí příkazu return a typ hodnoty je určen návratovým typem funkce. Návratový typ může být jakýkoliv primitivní datový typ (např. bool, int, double) a jiné typy, které ještě neznáme. Návratovým typem funkce nemůže být pole.
Také jsme se už setkali s několika případy, kdy funkce ukládala více než jednu hodnotu (scanf) nebo kdy jsme sami tušili, že by bylo vhodné vrátit z funkce více než jednu hodnotu (funkce na výpočet minima a maxima z řady čísel). Uložení více vypočtených hodnot může funkce řešit pomocí pointrů:
// funkce, ktera vydeli dve cela cisla a ulozi vysledek i zbytek do pameti odkazovane pointry quot a rem void div(int a, int b, int * quot, int * rem) { *qout = a / b; *rem = a % b; } // volani funkce - pri tretim a ctvrtem parametru se uzije operatoru reference int x = 3; int qu, re; div(x, 2, &qu, &re); // nyni x == 3, quot == 1 a rem == 1
Čeho si výše všimnout? První a druhý parametr jsou předávány hodnotou - hodnota proměnné x (3) je zkopírována při volání funkce add do jejího argumentu a. Stejně je tomu s konstantou 2. Třetí a čtvrtý parametr jsou předávány jako pointry na proměnné qu a re. Při volání add se zkopíruje adresa proměnných qu do quot a re do rem, takže např. pointer quot pak ukazuje na oblast paměti, kde leží proměnná qu. Pomocí pointru quot pak obsah proměnné qu změníme. Naopak jakkákoliv změna hodnot argumentů a a b má efekt pouze ve funkci div.
Velmi podobné je to s polem, vždy se předává adresa pole v paměti, nikoliv hodnoty jeho prvků:
// funkce vynuluje vektor predany jako parametr void zeros(int vector[], int lenght) { for (int i = 0; i < lenght; i++) { vector[i] = 0; } }