11.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
#include <iostream>
using namespace std;
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
}
int main () // 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
return 0;
}
---------------------------------------------
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
#include <iostream>
using namespace std;
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
r=a*b;
return (r); // visszatérési érték megadása
}
int main () // Itt kezdődik a fő-ág. Ennek muszáj int-nek lennie!
{
float z=1; // deklaráció
for (int n=1; n<11;n++)
{
z = szorzas (z,2); //Függvény meghívása
}
cout << "2^10 = " << z; // Végeredmény kiírása
return 0;
}
---------------------------------------------
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
#include <iostream>
using namespace std;
void kiiras ()
{
cout << "Eljaras vagyok!";
}
int main ()
{
kiiras();
return 0;
}
---------------------------------------------
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
#include <iostream>
using namespace std;
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 main ()
{
int x=1, y=3, z=7;
duplaz (x, y, z);
cout << "x=" << x << ", y=" << y << ", z=" << z;
return 0;
}
---------------------------------------------
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
#include <iostream>
using namespace std;
void szamelkov (int szam, int& elozo, int& kovetkezo)
{
elozo = szam-1;
kovetkezo = szam+1;
}
int main ()
{
int x=10, y, z;
cout <<" A szam: " << x;
szamelkov (x, y, z);
cout << ", Elo:zo:=" << y << ", Ko:vetkezo:=" << z;
return 0;
}
---------------------------------------------
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!
---------------------------------------------
// Alapértelmezett paraméter
#include <iostream>
using namespace std;
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);
}
int main ()
{
cout << szorzas (12); // Nincs megadva a szorzó
cout << endl;
cout << szorzas (20,4); // Itt viszont megadtuk!
return 0;
}
---------------------------------------------
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
#include <iostream>
using namespace std;
int operate (int a, int b) // Ez egészként lesz végrehajtva.
{
return (a*b);
}
float operate (float a, float b) // Ez pedig valósként.
{
return (a/b);
}
int main ()
{
int x=5,y=2;
float n=5.0,m=2.0;
cout << operate (x,y);
cout << "\n";
cout << operate (n,m);
cout << "\n";
return 0;
}
---------------------------------------------
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!
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
#include <iostream>
using namespace std;
long factor (long a)
{
if (a > 1)
return (a * factor (a-1));
else
return (1);
}
int main ()
{
long szam;
cout << "Irjon be egy szamot: ";
cin >> szam;
cout << szam << "! = " << factor (szam);
return 0;
}
---------------------------------------------
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 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
#include <iostream>
using namespace std;
void ps (int a); // Előre megnyugtatom a keresőt, hogy lesz ilyen függvény.
void ptlan (int a);
int main ()
{
int i;
do {
cout << "Kerek egy szamot (0-val kilephet): ";
cin >> i;
ps (i);
} while (i!=0);
return 0;
}
void ptlan (int a) // Íme, itt van az elöl hiányzó függvény.
{
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);
}
---------------------------------------------
12. 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: ax2+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.
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 <iostream>
#include <math.h> // Ez a szükséges matematikai műveleteket tartalmazza
using namespace std;
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;
}
int main ()
{
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
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!"; // Ilyenkor nincs megoldás
}
else
{ // Itt lesz megoldás
d1 = sqrt(d); // Gyökvonás
cout <<"\n Gyo:ke: " <<d1; // Biztonsági kiííratás
if (a!=0) // Ha a nevező nem 0, akkor lesz ez az ág aktív
{
x1 = (-b+d1)/(2*a); // Elsö gyök kiszámolása
cout <<"\n x1= " <<x1;
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
}
}
return 0;
}
---------------------------------------------
Észre kell venni, hogy itt a megszokott "#include <iostream>" mellett egy újabb elem bekerült a program legelejére. Az új elem: "#include <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 <iostream>
#include <math.h>
using namespace std;
#define PI 3.14159265
int main ()
{
long double i=0, j;
do
{
j = sin(i*PI/180);
cout <<"\n sin(" <<i <<")= " <<j;
i += 5;
}
while (i<361);
return 0;
}
---------------------------------------------
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:
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 hátvá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:
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