Függvények használata; tömbök függvényekben; a korábbi programozási tételes feladatok újrafogalmazása függvényekkel.
{esszé}
Fogalmazza meg, hogy a mi a különbség a függvény formális és aktuális paraméterei között! Adjon példát egy függvény fejsorára, és ugyanennek a függvénynek a hívására! A híváshoz ne felejtse el leírni a megfelelő paraméterek deklarációját!
{/esszé}
A függvény programozásbeli fogalmának lényege a következő: egy olyan viszonylagosan zárt, önálló programegység, amely a bemenetéül szolgáló adatok felhasználásával előállítja a kimenetéül meghatározott halmazba tartozó értéket.
A bemenetéül szolgáló adatokat általában az argumentumában soroljuk föl, amelyek neve, sorrendje és típusa kötött. A kimenetét alkotó értékek halmazának a függvény ún. értéktípusa felel meg.
A viszonylagos zártsága arra utal, hogy általában csak a paraméterein keresztül tartja a kapcsolatot a környezetével. Bizonyos kényelmi okok miatt lesz példa arra, hogy egy függvény a program paramétereit közvetlenül, azaz nem a paraméterezés útján éri el.
Az önálló jelző pedig azt fejezi ki, hogy jól meghatározható az a (rész)feladat, amelyet meg kell oldania, így minden olyan programba változatlanul beilleszthető, amelyben ez a részfeladat fölvetődik. Így válik hasznos eszközévé a felülről lefelé történő programtervezésnek.
A függvények a programozás során (is) kétféle szituációban bukkan(hat)nak föl: amikor fölhasználjuk, és amikor definiáljuk azokat. Amikor fölhasználjuk, akkor a paraméterei azok az objektumok, amelyekkel dolgoznia kell. Ezeket a paramétereket aktuális paramétereknek nevezik. Amikor definiáljuk a függvényszámítást, akkor a paraméterek azt írják le, hogy milyen módon játszanak szerepet a számításban. Ezek az ún. formális paraméterek.
A specifikációban a függvényekre hivatkozás nem különbözik a matematikában megszokottól. A definiáláshoz a specifikációban egy külön rész szolgál, amelyben először az adott függvény értelmezési tartományát és értékkészletét adjuk meg (ez az ún. szignatúra), majd a leképezés szabályát. Egy korábbi gyakorlatból idézzük az alábbi specifikációrészletet:
…
Utófeltétel
PrímN∈[10N-1+1 .. 10N-1] és (PrímN Mod 10)=1 és Prím?(PrímN) és …
Definíció
Prim?:Egész→Logikai
Prím?(x):= ∀i∈[2..Gyök(x)]: nem (i | x)
Algoritmizálás közben a hivatkozás a szokásnak megfelelően történik, a definiáláshoz viszont egy új struktogramot kell szentelni, amelyben két újdonság van:
1) a fejsor – a függvény nevén felül – felsorolja az értelmezési tartományt megtestesítő paraméterek lokális neveit, azaz amikkel a függvény törzsén belül lehet rájuk hivatkozni;
2) a függvény értékét egy, a függvény nevével megegyező nevű változó képviseli, a neki adott érték lesz a függvény végső (visszaadott) értéke.
A függvénydefiniálásra egy struktogram-példa:

A C++ kódban a hivatkozás az eddigiekhez hasonlóan egyszerű: függvényNév(paraméterek). A definiáláshoz alapelv, hogy a fordítóprogramnak már az előtt ismernie kell a függvényhívás szintaktikáját, azaz a függvény szignatúráját, mielőtt használnánk, hivatkoznánk rá. Természetesen a fordításhoz a függvény működését leíró törzs is kell, de azt szabad halogatni, a forrásprogram végére hagyni. Így a javasolt programszerkezet az alábbi:
#include <iostream>
using namespace std;
… fv-szignatúrák …
int main()
{
… main-törzs …
return 0;
}
… fv-definiciók …
A fv-szignatúra szintaxisa a deklarációkéra emlékeztet; újdonság a paraméterezés jelölése:
típ fv-azonosító(formális paraméterezés);
Jegyezzük azonban meg, hogy a paraméterezés még akkor sem hagyható el, ha valójában nincs paramétere, azaz a zárójelek nem hagyhatók el!
A fv-definíció szintaxisa – nem meglepő módon – nagyban emlékeztet a már megismert main() függvény szintaxisára:
típ fv-azonosító(formális paraméterezés)
{
… a fv-törzs … //megegyezik a main-törzs szintaxisával
return fv-érték; //fv-érték: egy kifejezés, amely a visszaadandó
//értéket képviseli; típusa: típ
}
Mindennél többet mond egy konkrét kód-példa:
#include <iostream>
using namespace std;
bool PrimE(int x);
int main()
{
…
if (PrimE(N))
{
…
}
…
return 0;
}
bool PrimE(int x)
{
int i=2;
while (i*i<=x && i%x!=0)
{
++i;
}
return i*i>x;
}
Megjegyzések a konkrét kódhoz:
A függvényeknek van egy olyan fajtája, amely nem úgy ad vissza értéket, mint egy függvény, hanem paraméterei közt van olyan, amely értékét módosítja. Ezeket eljárásfüggvényeknek (vagy egyszerűen: eljárásoknak) nevezik. Ekkor a típusaként a void (üres, mentes jelentésű) szót kell megadni. A return utasításra ekkor nincs szükség.
Kétféle számunkra érdekes paraméterátadási módszer van: az érték szerinti, illetve a hivatkozás szerinti.
Az érték szerinti paraméterátadás lényege: a formális paraméterből keletkezett lokális változóba másolódik a híváskor az aktuális paraméter értéke, így ennek a törzsön belüli megváltozása nincs hatással az aktuális paraméterre.
A hivatkozás szerinti paraméterátadás lényege: a formális paraméterbe az aktuális paraméter címe (rá való hivatkozás) kerül, a lokális néven is elérhetővé válik. Így értelemszerűen akár meg változtatni is képes azt.
A függvény szignatúrájában jelölni kell, hogy a paraméterátadás melyik fajtáját írjuk elő. Hogy ezt miként kell tükrözni a C++ nyelvben, írjuk le alább.
Skalárváltozók esetén az érték szerinti: típus formális-paraméter; a hivatkozás szerinti: típus &formális-paraméter.
int max(int x, int y)
vagy ugyanez eljárásfüggvényként:
void max(int x, int y, int &maxxy)
Tömbváltozók esetén az érték szerinti: const típus formális-paraméter[]; a hivatkozás szerinti: típus formális-paraméter[]. Vegyük észre a C++ filozófiaváltását: tömböknél a hivatkozás szerintit tekinti alapvetőbbnek (⇒egyszerűbben leírhatónak).
void ki_int_tomb(const int x[], int n) void be_int_tomb(int x[], int &n, int maxN)
Az animáció bemutatja a függvények használatát.
Készítsük el azt az eljárást, amely beolvas egy min..max közötti int számot! Ha a max<min, az jelentse azt, hogy max=+∞! Az eljárás szignatúrája az alábbi:
void be_int(string kerdes, int &n, int min, int max, string uzenet)
Bemenet
kérdés,üzenet:Szöveg, min,max:Egész
Kimenet
n:Egész
Előfeltétel
−
Utófeltétel
∃i>0: n=cini és (min≤max → n∈[min..max]) és
(min>max → n≥min) és
∀j∈[1..i): coutj=üzenet
Magyarázat az utófeltételhez: A ∃i: n=cini azt fejezi ki, hogy előbb-utóbb (pontosabban az i. próbálkozásra) a billentyűzetre kerül számként értelmezhető jelsorozat, amely teljesíti a határok által megfogalmazott feltételeket. Az addigi elvárást fejezi ki a ∀j∈[1..i): coutj=üzenet feltételrész, amit így olvashatunk: az első helyes inputig (az i. próbálkozást megelőzően) a konzol outputra a megadott (hiba-)üzenet kerül.

//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet)
{
bool hiba;
string tmp;
do
{
if (max>=min)
{
cout << kerdes << " (" << min << ".." << max << "):"; cin >> n;
hiba=cin.fail() || n<min || n>max;
}
else
{
cout << kerdes << " (" << min << "..):"; cin >> n;
hiba=cin.fail() || n<min;
}
if (hiba)
{
cout << uzenet << endl;
cin.clear(); getline(cin,tmp,'\n');
}
}while(hiba);
}
Készítsük el azt az eljárást, amely megcserél két int értéket! Az eljárás szignatúrája:
void csere_int(int &a, int &b)
Bemenet
a,b:Egész
Kimenet
a,b:Egész
Előfeltétel
−
Utófeltétel
a’=b és b’=a

Éltünk a korábban bevezetett struktogram egyszerűsítési lehetőséggel, amely során több értékadást is egyetlen dobozba sűrítettünk.
//csere: a<=>b
void csere_int(int &a, int &b)
{
int c=a; a=b; b=c;
}
Építsük össze az előbbi két eljárást egy próbaprogramba amely csak pozitív számokat fogad el!
Bemenet
A,B:Egész
Kimenet
A,B:Egész
Előfeltétel
−
Utófeltétel
A’=B és B’=A
Banálisan egyszerű: a nem algoritmizálandó beolvasáson és kiíráson túl a csereeljárás meghívását tartalmazza, valamint a felhasznált két eljárás struktogramját. Ezért nem részletezzük.
#include <iostream>
using namespace std;
//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet);
//csere: a<=>b
void csere_int(int &a, int &b);
int main()
{
int A,B;
be_int("Kérem az 'A'-t (pozitív egész):",A,1,0,"Nem jó szám!");
be_int("Kérem a 'B'-t (pozitív egész):",B,1,0,"Nem jó szám!");
csere_int(A,B);
cout << "Az 'A':" << A << endl;
cout << "A 'B':" << B << endl;
return 0;
}
//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet)
{
bool hiba;
string tmp;
do
{
if (max>=min)
{
cout << kerdes << " (" << min << ".." << max << "):"; cin >> n;
hiba=cin.fail() || n<min || n>max;
}
else
{
cout << kerdes << " (" << min << "..):"; cin >> n;
hiba=cin.fail() || n<min;
}
if (hiba)
{
cout << uzenet << endl;
cin.clear(); getline(cin,tmp,'\n');
}
}while(hiba);
}
//csere: a<=>b
void csere_int(int &a, int &b)
{
int c=a; a=b; b=c;
}
Egy korábbi leckében a következő feladat került szóba, amelyet ismét előveszünk, de az „újdonságok” kedvéért:
Határozzuk meg az első 1-re végződő N jegyű prímszámot! Oldjuk meg most függvények felhasználásával!
Ezt a specifikációt készítettük akkor:
Bemenet
N:Egész
Kimenet
PrímN:Egész
Előfeltétel
N>1 [ilyen garantáltan létezik]
Utófeltétel
PrímN∈[10N-1+1 .. 10N-1] és (PrímN Mod 10)=1 és Prím?(PrímN) és
∀pm∈[10N-1+1 .. 10N-1]: (pm Mod 10)=1 és Prím?(pm) → pm≥PrímN
Definíció
Prim?:Egész→Logikai
Prím?(x):= ∀i∈[2..Gyök(x)]: nem (i | x)
Az akkori algoritmusok alapján az alábbiakat alkothatjuk meg. Csak a legfelsőbb szintű, lényegi algoritmusban (PrímKeresés-ben) van elenyésző változás. A változást épp a függvénnyé alakítás okozza.
A kiinduló algoritmus | → | A függvényesített algoritmus |
|---|---|---|
![]() | → | ![]() |
Változatlanul hagyhatjuk a Prím?() függvényt:

A kódoláskor felhasználtuk a korábban elkészített egész-beolvasó eljárást és a PrimE() függvényt. Az algoritmus alapján kódoltuk a PrimKereses() függvényt, és egy később is felhasználható, billentyűlenyomásra várakoztató eljárást is készítettünk.
#include <iostream>
#include <windows.h>
using namespace std;
//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet);
//n-jegyű prímet keres:
int PrimKereses(int n);
//eldönti: x prímszám-e
int PrimE(int x);
//billentyű-lenyomásra várakozik
void billreVar();
int main()
{
int N; //a jegyek száma
int PrimN;//N jegyű prím
be_int("A jegyek száma (>1):",N,2,0,"Nem jó számhossz!");
PrimN=PrimKereses(N);
cout << "Az első 1-re végződő " << N << "-jegyű prím:"
<< PrimN << endl;
billreVar();
return 0;
}
//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet)
{
bool hiba;
string tmp;
do
{
if (max>=min)
{
cout << kerdes << " (" << min << ".." << max << "):"; cin >> n;
hiba=cin.fail() || n<min || n>max;
}
else
{
cout << kerdes << " (" << min << "..):"; cin >> n;
hiba=cin.fail() || n<min;
}
if (hiba)
{
cout << uzenet << endl;
cin.clear(); getline(cin,tmp,'\n');
}
}
while(hiba);
}
//n-jegyű prímet keres:
int PrimKereses(int n)
{
//10^(n-1) (n>1):
int tn=1;
for (int i=1;i<n;++i)
{
tn*=10;
}
int i=tn+1;
while (!PrimE(i))
{
i+=10;
}
return i;
}
//eldönti: x prímszám-e
int PrimE(int x)
{
int i=2;
while (i*i<=x && i%x!=0)
{
++i;
}
return i*i>x;
}
//billentyű-lenyomásra várakozik
void billreVar()
{
system("pause");//windows esetében!
}
Írjuk meg azt az eljáráspárt, amely beolvas, illetve kiír egy N elemű tömböt! A tömb legyen statikusan deklarálva. Szignatúráik legyenek az alábbiak:
void be_int_tomb(int x[], int &n, int maxN) void ki_int_tomb(const int x[], int n)
Majd ezek kipróbálására a következő feladatú eljárást is készítsük el: a beolvasott elemek sorrendjének a megfordítsa! Használjuk föl a korábbi csere(…) eljárást is! A megfordítást végezze az alábbi alprogram (a paraméterezés „megálmodása” is a feladat része):
void megfordit_int_tomb( ??? , ??? )
Adja meg egy természetes szám prímtényezős felbontását! A programban használjuk az előbbiekben létrehozott eljárásokat, valamint egy újat, amely egy tömbben sorolja föl a prímtényezőket!
Válasszon ki az eddigi gyakorlatokon elkészített programok közül néhányat, és írja át ezeket a fentiek szellemében úgy, hogy a beolvasást, a lényegi tevékenységet és a kiírást függvényekkel valósítja meg!
![]() ![]() |
![]() |
![]() |