9.fejezet: Függvények és külső eljárások Úgy érzem, éppen itt az ideje, hogy összeszedjünk mindent, amit az eljárásokról, illetve a függvényekről tudni kell és rendszerezzük!nos, az alapvető eljárás ugye a main(), ami alapesetben nem ad végeredményt, mivel return 0-val tér vissza. Először is lássunk egy általános függvény-leírást! típus név ( paraméter1, paraméter2,...) törzs Itt a következők szerepelnek: típus: ezt a fajta adatot adja vissza a függvény meghívásakor. név: ez a függvény elnevezése lesz, azaz így lehet meghívni. paraméter1, paraméret2,... (amennyi csak szükséges): minden paraméter tartalmaz egy alapvető típust, mint egy helyi változó deklarációjánál (például: int x); továbbá ezek a változók azonnal használhatók a függvényen belül. Segítségükkel lehet a függvény paraméterek segítségével meghívni. A különböző paramétereket vesszőkkel kell elválasztani. törzs: ezt kell mindenképpen végrehajtani. Lássunk erre egy gyakorlati példát! // függvény-minta 1 / 16
int osszeadas (int a, int b) // ez lesz a függvény neve és ezek a paraméterek int r; // belső változó deklarálása r=a+b; return (r); // visszatérési érték megadása // Itt kezdődik a fő-ág. int z; // deklaráció z = osszeadas (5,3); //Függvény meghívása cout << "Eredmeny: " << z; // Végeredmény kiírása Ezt az összeadást simán meg lehet valósítani a hagyományos "+" jellel is, de így legalább könnyebben meg lehet érteni a C++ függvényeinek működési elvét! Második példánkban lássunk egy hatványozást, nevezetesen 2-nek a 10-dik hatványát. Itt magát a külső függvényt 10-szer fogjuk meghívni. // 2. függvény-minta float szorzas (float a, float b) // ez lesz a függvény neve és ezek a paraméterek float r; // belső változó deklarálása 2 / 16
r=a*b; return (r); // visszatérési érték megadása float z=1; for (int n=1; n<11;n++) z = szorzas (z,2); cout << "2^10 = " << z; // Itt kezdődik a fő-ág. Ennek muszáj int-nek lennie! // deklaráció //Függvény meghívása // Végeredmény kiírása Nagyon fontos megérteni, hogy a z változó a main() körében érvényesül, míg az r változó csak a szorzas() körében. Vannak különleges függvények, amelyek nem adnak vissza értéket. Ezt eljárásnak hívjuk. Neve: void(). Persze ezeknek is lehet paramétere, de nem kötelező! // Eljárás-minta void kiiras () cout << "Eljaras vagyok!"; 3 / 16
kiiras(); Még valami: első látásra furcsának tűnhet, hogy miért kell kirakni a függvény neve után az üres zárójelet, de a C++ lelkivilága ezt megköveteli... Ebben például nincsen paraméter. Szeretném felhívni a figyelmet arra a tényre, hogy a kiiras(); szabályos, de a kiiras; nem. Következő példánkban újabb minta jön a C++ végtelen rugalmasságára! // Paraméterek érték szerinti átadása void duplaz (int& a, int& b, int& c) // Így a paraméterek értékei megváltoztathatók lesznek! a*=2; // Egyszerű duplázás b*=2; c*=2; int x=1, y=3, z=7; duplaz (x, y, z); cout << "x=" << x << ", y=" << y << ", z=" << z; 4 / 16
Itt, mivel eljárást hívtunk és nem függvényt, így nem lesz egyetlen visszaadott érték. Ennek ellenére a paraméterek értékét meg tudjuk változtatni, mivel a paraméterek meghívásakor az int után "&" jel került. A két eljárás-hívást természetesen keverhetjük is! Az alábbi példában egy tetszőleges egész szám szomszédait adjuk vissza (változtatható értékként), míg maga a szám nem változik! // Számszomszédok void szamelkov (int szam, int& elozo, int& kovetkezo) elozo = szam-1; kovetkezo = szam+1; int x=10, y, z; cout <<" A szam: " << x; szamelkov (x, y, z); cout << ", Elo:zo:=" << y << ", Ko:vetkezo:=" << z; Persze lehet olyan kérés is, hogy egy függvény paramétereiben egy vagy több eleve adott értékű legyen. Ez kicsit több mozgásteret tesz lehetővé, mivel így variálhatunk például a paraméterek számával (is). Lássuk a következő példát! 5 / 16
// Alapértelmezett paraméter int szorzas (int a, int b=2) // b=2 alapértelmezett, ha nem jön meg paraméterként! int r; r=a*b; return (r); cout << szorzas (12); cout << endl; cout << szorzas (20,4); // Nincs megadva a szorzó // Itt viszont megadtuk! Itt a szorzas() függvény paramétere a szorzandó, amit mindenképpen meg kell adni, valamint a szorzó, amit ha nem adunk meg, akkor a függvény 2-nek veszi. Többször is láthattuk, hogy a C++ rettenetesen rugalmas - jöjjön hát most egy újabb példa! // Többszörös elnevezésű függvények 6 / 16
int operate (int a, int b) return (a*b); // Ez egészként lesz végrehajtva. float operate (float a, float b) return (a/b); // Ez pedig valósként. int x=5,y=2; float n=5.0,m=2.0; cout << operate (x,y); cout << "n"; cout << operate (n,m); cout << "n"; Itt két különböző függvényt definiáltunk azonos névvel, ám az egyikük csak egész értékeket fogad, míg a másik csak valóst. A fordító a paramétereket áttekintve tudja, hogy melyiket mikor kell hívnia, így nem lesz zavar a meghívásukban. Szeretném mindazonáltal megjegyezni, hogy a függvények ilyen típusú többszörös elnevezésével csínján kell bánni, mivel a visszatérési érték különbözősége mellett legalább egy paraméternek különbözőnek kell lennie! 7 / 16
Következő függvény-típusunk a rekurzív, azaz a saját magát meghívó függvény. Ennek alapesete a faktoriális. Például: 6! = 1*2*3*4*5*6. // Faktoriális long factor (long a) if (a > 1) return (a * factor (a-1)); else return (1); long szam; cout << "Irjon be egy szamot: "; cin >> szam; cout << szam << "! = " << factor (szam); Mint látható, sok programozási nyelvvel szemben a C++ logikában alapvetően bent van a rekurzió, azaz a saját függvény sokszoros meghívási lehetősége. A programot futtatva némi próbálkozás után látható, hogy a helyes értéket csak akkor kapjuk meg, ha 0 és 33 közötti egész számot írunk be. Ne feledjük el, hogy a float azért egy viszonylag kis terjedelmű valós számot képes csak tárolni. Nagyobb értékek tárolásához ki kell cserélnünk a típust valami 8 / 16
másra, például "long double"-re. Elő-deklaráció esetén nem kötelező megadni a leendő változók nevét, csak a típusát. Hogy kicsit érthető legyen, lássunk egy példát, ahol mindkettő elő-deklaráció helyes: int protofuggveny (int first, int second); int protofuggveny (int, int); Nézzünk erre egy minta-programot: // Függvények prototípusának deklarálása void ps (int a); // Előre megnyugtatom a keresőt, hogy lesz ilyen függvény. void ptlan (int a); int i; do cout << "Kerek egy szamot (0-val kilephet): "; cin >> i; ps (i); while (i!=0); void ptlan (int a) // Íme, itt van az elöl hiányzó függvény. 9 / 16
if ((a%2)!=0) cout << "A szam paratlan.n"; else ps (a); void ps (int a) if ((a%2)==0) cout << "A szam paros.n"; else ptlan (a); 10. fejezet: Gyakorlati feladatok és matematika Ebben a részben alig-alig lesz új elem, csak a jelenlegi tudásunkat mélyítjük el. Első feladatként próbáljuk meg a klasszikus másodfokú egyenlet megoldóképletét összehozni. Matematikai ismétlésként: aki nem tudná, a másodfokú egyenlet általános alakja: ax 2 +bx+c=0, ahol a,b,c valós számok. Maga a megoldóképlet pedig: A dologhoz először is úgy érdemes nekilátni, hogy létrehozunk egy önálló eljárást a gyök alatti kifejezés (diszkriminánsnak hívják) kiszámolására. 10 / 16
double diszkr (double a1, double b1, double c1) double dr; dr = (b1*b1)-(4*a1*c1); return dr; Érdemes észrevenni, hogy a dr egyetlen sorral simán kiszámítható. Viszont visszatérési értéket kell képezni, mivel függvényként hívjuk meg. Ha ez megvan, akkor a többi kicsit könnyebb lesz. A kommenteket érdemes elolvasni! // Másodfokú egyenlet megoldóképlete #include <math.h> // Ez a szükséges matematikai műveleteket tartalmazza double diszkr (double a1, double b1, double c1) // Ez a külső függvény, a diszkrimináns kiszámolása double dr; dr = (b1*b1)-(4*a1*c1); return dr; double a,b,c,d,d1,x1,x2; // Hagyományos változók deklarálása // Adatok bekérése cout << "Masodfoku egyenlet megoldokeplete." <<endl; cout << "A'ltalanos formula: ax^2+bx+c=0" <<endl; cout << "Kerem az 'a' erteket: "; cin >> a; // a bekérése 11 / 16
cout <<"Kerem a 'b' erteket: "; cin >> b; // b bekérése cout <<"Kerem a 'c' erteket: "; cin >> c; // c bekérése d = diszkr(a,b,c); // diszkrimináns meghívása cout <<"n Diszkriminans erteke: " <<d; // biztonságképpen irassuk ki a disztrmináns értékét if (d<0) cout <<"nnegativ szam van a gyo:kjel alatt. Nincs valos megoldas!"; megoldás // Ilyenkor nincs else // Itt lesz megoldás d1 = sqrt(d); cout <<"n Gyo:ke: " <<d1; if (a!=0) x1 = (-b+d1)/(2*a); cout <<"n x1= " <<x1; // Gyökvonás // Biztonsági kiííratás // Ha a nevező nem 0, akkor lesz ez az ág aktív // Elsö gyök kiszámolása x2 = (-b-d1)/(2*a); // Második gyök kiszámolása cout <<"n x2= " <<x2; else cout <<"<nez nem masodfoku egyenlet!"; //A nevező itt 0 Észre kell venni, hogy itt a megszokott "" mellett egy újabb elem bekerült a program legelejére. Az új elem: "#i 12 / 16
nclude <math.h>", melyet azért kellett becsatolni, hogy a matematikai függvényeket, például a gyökvonást is lehessen használni! Második példánkban írassuk ki a szögek szinuszát 0-tól 360 fokig (5-ösével). Bár ehhez a legegyszerűbb lenne egy számláló (for-)ciklus, be most pusztán a gyakorlás kedvéért szeretnék hátultesztelő ciklust javasolni! A kiíratáshoz tudni kell, hogy a sin() függvény három különféle bemenetet, illetve ugyanolyan kimenetet engedélyez. Ezek: float, double, long double. Még valami: a függvény nem szöget, hanem radiánba átszámolt értékeket kér bemenetként. Ehhez a szög értékét meg kell szorozni a PI értékével, illetve el kell osztani 180-cal. A kész függvény-kódban érdemes volt előre definiálni a PI értékét, mert így nem kell vele később foglalkozni! Tehát a teljes kód: // Szinusz-példa #include <math.h> #define PI 3.14159265 long double i=0, j; do j = sin(i*pi/180); cout <<"n sin(" <<i <<")= " <<j; i += 5; while (i<361); 13 / 16
Gyakorlati javaslat: ha az adott nyelvben nincsen direkt átszámító függvény fokról radiánba, illetve vissza, akkor érdemes ezt külső függvényként külön definiálni. További matematikai függvények a math.h könyvtárban: (Forrás: http://www.cplusplus.com/reference/clibrary/cmath/ ) Trigonometrikus függvények: cos - cosinusz sin - sinusz tan - tangens acos - arcus cosinusz asin - arcus sinusz atan - arcus tangens atan2 - arcus tangens két paraméterrel (radiánban, kér koordinátával adja vissza a -pi +pi értékek között) Hiperbolikus függvények: 14 / 16
cosh - cosinusz hiperbolikusz sinh - sinusz hiperbolikusz tanh - tangens hiperbolikusz Exponenciális és logaritmikus függvények: exp - Exponenciális (e hatványa), ahol e=2,71828. frexp - Egy hatványt felbont valós szorzóra és 2 egész kitevőjű hatványára. ldexp - Előzőhöz hasonló, csak eltérő lehetőségekkel log - Egy szám természetes (azaz e-alapú) logaritmusát adja meg. log10-10-alapú logaritmus. modf - Egy szám felbontása egész és törtrészre. Hatvány-függvények: 15 / 16
pow - Egy szám hatványa (Paraméterek: alap,kitevő) sqrt - Négyzetgyök Vegyes függvények: ceil - A legkisebb egész szám, ami nem kisebb, mint a paraméter. fabs - Abszolút érték floor - A legnagyobb egész szám, ami nem nagyobb, mint a paraméter. fmod - Osztási maradék Folytatás itt! 16 / 16