Alprogramok, függvények I PDF
Document Details
Uploaded by Deleted User
Tags
Summary
This document details a course on C programming, specifically covering procedures, and functions. Including examples and illustrations.
Full Transcript
Kvíz http://www.menti.com aki nem az azonosítóját használja, mindenki a saját azonosítójával (bbbbnnnn) lépjen be ! annak nem fogjuk tudni beírni a pontokat a kvízre 1275 8726 A...
Kvíz http://www.menti.com aki nem az azonosítóját használja, mindenki a saját azonosítójával (bbbbnnnn) lépjen be ! annak nem fogjuk tudni beírni a pontokat a kvízre 1275 8726 Alprogramok, függvények I KURZUS Alprogramok, azaz függvények programok építőkövei utasítás gyűjtemény közös név alatt a függvények meghívásakor kerülnek lefuttatásra a forráskód alprogramokba/függvényekbe való rendezése megkönnyíti a forráskód rendezését/áttekintését elősegíti a nagy és komplex programok fejlesztését megírás, megértés, módosítás, tesztelés és karbantartás eddig a programokat a következő képpen írtuk főprogram: a main – amely nem hiányozhat egy C programból sem a standard C könyvtárból felhasznált függvények – amelyeket a main programrészből hívtunk meg a megfelelő header file beillesztésével #include int maximum(int a, int b) { return a > b ? a : b; } Egy program szerkezete int minimum(int a, int b) { egy program egy vagy több függvényből áll return a < b ? a : b; } hasonlóak a matematikai függvényekhez #include int osszeg(int a, int b) int main() { { return a + b; int a, b, max, min, sum; } printf(”> adj meg 2 egesz szamot: "); int main() scanf("%d %d", &a, &b); { max = a > b ? a : b; int a, b, max, min, sum; min = a < b ? a : b; printf(”> adj meg 2 egesz szamot: "); sum = a + b; scanf("%d %d", &a, &b); printf(”> max(%d, %d) = %d\n", a, b, max); printf(”> max(%d, %d) = %d\n", a, b, maximum(a, b)); printf(”> min(%d, %d) = %d\n", a, b, min); printf(”> min(%d, %d) = %d\n", a, b, mininum(a, b)); printf(”> sum(%d, %d) = %d\n", a, b, sum); printf(”> sum(%d, %d) = %d\n”`, a, b, osszeg(a, b)); return 0; return 0; } } Definíció és meghívás alprogram/függvény felépítése visszatérésiTípus függvénynév(paraméterlista) { utasítás(ok); return visszatérésiÉrték; } visszatérés a függvényből: a return utasításkor vagy az utasítás lista végrehajtás végeztével float atlag(int a, int b) { fejléc int main() { return (a + b)/2.0; int a, b; } törzs printf(”> adj meg 2 egesz szamot: "); scanf("%d %d", &a, &b); printf(”> atlag(%d, %d) = %.2f\n", a, b, atlag(a, b)); return 0; } A fejléc a függvény visszatérési értékének típusa pl. int, float, char, double, stb. visszatérésiTípus függvénynév(paraméterlista) ha a függvény nem térít vissza értéket, akkor void { utasítás(ok); KORLÁTOZÁS nem téríthető vissza tömb (array) vagy más függvény return visszatérésiÉrték; } ha hiányzik a visszatérési érték típusa C89 implicit úgy értelmezi, hogy a függvény visszatérít egy egész értéket C99 és C11 hibát jelez int tombElemeinekOsszege(int tomb[], int hossz) a függvény egyedi neve int atlag(int a, int b, int c) hasonlóképpen egyedi, mint egy változó azonosítója int main(void) int main() paraméterek listája kerek zárójelben adjuk meg, tartalmazza az adattípust és a változó azonosítóját, vesszővel választjuk el őket az adattípust mindegyik változó esetén fel kell tüntetni, még akkor is, hogyha ugyan azon típusúak ha a lista üres, azaz a függvény nem kap paramétereket, akkor a void típust használjuk vagy üresen hagyjuk A függvények meghívása a függvény hívás során megadjuk a függvény nevét és kerek zárójelben megadjuk a paraméterek értékét, azaz az aktuális paraméterek listáját függvénynév(paraméterlista); a függvényhívás egy kifejezés szerepelhet, mint egy operandus egy kifejezésben, vagy mint egy aktuális paraméter egy másik függvény hívásakor egy önálló utasításként is szerepelhet, vagyis a hívást a ; (pontosvessző) karakterrel zárjuk fejléc hívás int faktorialis(int szam) e = faktorialis(10); #include int faktorialis(int szam) { faktorialis(10) int f = 1; Példa int i; szam: 10 for(i = 2; i adj meg egy szamot: 10 int szam; > 10! = 3628800 int eredmeny; printf(”> adj meg egy szamot: "); scanf("%d", &szam); main() eredmeny = faktorialis(szam); printf(”> %d! = %d\n", szam, eredmeny); szam: 10 eredmeny: 3628800 return 0; } Megjegyzések azon paramétereket amelyekkel az értékeket amelyekkel meghívásra kerül definiáljuk a függvényt formális egy függvény aktuális paramétereknek paramétereknek nevezzük, vagy csak hívjuk, vagy csak egyszerűen argumensek egyszerűen paraméterek int maximum(int a, int b) int main() { { return a > b ? a : b; int a, b; } printf(”> adj meg 2 egesz szamot: "); scanf("%d %d", &a, &b); printf(”> max(%d, %d) = %d\n", a, b, maximum(a, b)); return 0; } a formális és aktuális paraméterek szerepelhetnek ugyanazzal a névvel (lásd fenti példa) vagy akár különböző nevekkel az aktuális paramétereket ugyan abban a sorrendben adjuk meg, ahogy azok a formális paraméterek sorrendjében szerepelnek Megjegyzések a paraméterek átadása és feldolgozása tetszőleges sorrendben történik a C89 megengedi, hogy a függvény hívása megelőzze annak definícióját vagy deklarálását a C99 és C11 kötelezik a függvény definícióját vagy deklarálását annak hívása előtt kvíz 1. kérdés 1275 8726 A függvények deklarálása a függvényhívás pillanatában a fordítóprogramnak ismernie kell a függvény visszatérési értékének típusát és a formális paraméterek listáját, azaz a függvény prototípusát TEHÁT a függvényt a meghívás előtt deklarálni vagy definiálni kell DEKLARÁLÁS a függvény prototípusának a deklarálását jelenti, amely a függvény fejlécéből áll és amelyet ; (pontosvessző) zár le a prototípus meg kell egyezzen a definiáláskor megadott fejléccel void fuggveny(int i, float x, char c); ekvivalens prototípusok void fuggveny(int, float, char); a paraméterek változónevei elhagyhatók Láthatóság egy azonosító (változó, konstans, stb.) kizárólag abban a blokkban látható és elérhető, amelyben deklarálva volt: char c = 99; globális int main() { a teljes programban láthatók és elérhetők printf("%c\n", c); { a program tetszőleges helyén definiálhatók és módosíthatók int a = 5; printf("> %d\n", a); { lokális int a = 6; char c = 'A'; az a és c változók a függvényen/blokkon belül vannak deklarálva printf("> %d\n", a); értéke maszkolva van printf("> %c\n", c); a függvénybe/blokkba való belépéskor jönnek létre } } a végén megszűnnek értékük elveszik !!! printf("> %c\n", c); minden függvény/blokk kizárólag csak a sajátjait látja !!! } return 0; a függvény globális változókon, pointereken és a return utasításon keresztül tud információt visszaküldeni a meghívó függvényhez FIGYELEM változók maszkolása megnehezíti a forráskód megértését és javítását! #include char c = 98; int main() Példa { printf("> %c\n", c); b { maszkolas.c int a = 5; printf("> %d\n", a); 5 { int a = 6; char c = 'A'; printf("> %d\n", a); 6 printf("> %c\n", c); A } mennyi az a értéke itt? printf("> %d\n", a); 5 } kvíz 2. kérdés printf("> %c\n", c); b az a változó itt már nincs definiálva return 0; } 1275 8726 Program memóriacímzése 0xFFFFFFFF kernel 0xC0000000 stack (verem) lokális változók, függvények argumentumai, visszatérési címek heap (kupac) dinamikusan lefoglalt adatok BSS globális és statikus változók, amelyek nincsenek inicializálva adatszegmens inicializált globális és statikus változók, konstansok kódszegmens a program bináris kódja 0x08048000 0x00000000 Paraméterek átadása C nyelvben a paraméterek átadása kizárólag érték szerint történik az aktuális paraméter értéke bemásolásra kerül a meghívott függvény megfelelő formális paraméterébe #include int summa1N(int n); int main() { függvény prototípusa int n, sum; printf(”> adj meg egy egesz szamot: "); scanf("%d", &n); beolvasásra kerül az n változó értéke sum = summa1N(n); printf(”> az elso %d szam osszege: %d\n", n, sum); az n változó értéke átmásolásra kerül, a másolat értéke teljesen return 0; független a főprogramban levő n változó értékétől } int summa1N(int n) { pointer int s = 0; for(; n >= 0; s += n, n--); az n változó értéke változatlan marad return s; előny: megóvja a meghívó függvény változóit } hátrány: a másolás nem hatékony művelet nagy adatszegmensek esetén Paraméterek átadása – a klasszikus hiba #include void osszeg(int a, int b, int sum) { mivel a paraméterek lokális változók, ezért a sum = a + b; } ??? függvényből való visszatéréskor megszűnnek int main(void) emiatt paraméteren keresztül közvetlenül nem { int sum; lehet értéket visszaadni a függvényt meghívó programrésznek sum = 0; printf(”> elotte: %d\n", sum); a példában szereplő sum változó nem magát a osszeg(5, 6, sum); változót, hanem a sum változó értékének egy printf(”> utana: %d\n", sum); kvíz 3. kérdés másolatát kapja meg, vagyis 0-t } return 0; a függvényben ugyan a sum lokális változó felveszi az összeg helyes értékét, de megszűnik létezni a függvényből való visszatéréskor 1275 8726 Paraméterek átadása és az örökös kérdés: hogyan módosíthatná egy függvény a kapott paraméter értékét úgy, hogy a módosítás a meghívó függvényben is látható maradjon? pointerek 0xFFFFFFFF kernel void elsoFuggveny() { 0xC0000000 int a; masodikFuggveny(&a); main } meg kell stack (verem) egyezzenek int masodikFuggveny(int *p) { a elsoFuggveny... } p masodikFuggveny a masodikFuggveny képes módosítani az a változó értékét az elsoFuggveny-ből, ha van egy pointer amelyik az a változóra mutat: ismeri annak a címét átküldjük a &a értéket (az a változó címe) a masodikFuggveny-nek (ez érték szerinti átadás, ennek értéke nem változik meg a masodikFuggveny végrehajtása során) Paraméterek átadása #include #include pointeren kereztül történő átadás int fuggveny(int a); int main() { érték szerinti átadás int a; int main() scanf("%d", &a); { int a = 1, b; return 0; b = fuggveny(a); } return 0; } az a változó értéke bemásolásra kerül abban az esetben, ha szeretnénk az a egy lokális változóba a fuggveny változó értékét megváltoztatni, meg meghívásakor kell mondjuk a scanf függvénynek, az a értéke a main programrészben hogy hol találja az a változót: a &a nem változik címen Paraméterek átadása swap.c #include void swap(int *, int *); a formális paraméterek int típusú pointerek int main() { int a = 2; int b = 5; printf("> a = %d\tb = %d\n", a, b); példa swap(&a, &b); a függvény hívásakor átadjuk a változók címét printf("> a = %d\tb = %d\n", a, b); return 0; } void swap(int *p, int *q) { a függvény a * operátort használjuk, hogy hozzáférjünk a változók int tmp; tmp = *p; értékéhez, amelyet a továbbított címen tárolnak *p = *q; *p ekvivalens a főprogram a változójával *q = tmp; } *q ekvivalens a főprogram b változójával #include void min_max(int szam, int *min, int *max) { int m; if(szam < 10) { printf("> a legkisebb es legnagyobb szamjegy azonos : %d\n", szam); } Példa else { *max = szam%10; *min = *max; szam /= 10; min_max.c while(szam != 0) { m = szam%10; if(m > *max) keressük meg a legnagyobb és a { *max = m; legkisebb számjegyet egy egész } számban else if(m < *min) { *min = m; } szam /= 10; } } } int main() { int szam, min, max; printf("> adj meg egy egesz szamot\n"); printf("> szam : "); scanf("%d", &szam); min_max(szam, &min, &max); printf("> a %d szam legkisebb szamjegye %d, mig a legnagyobb %d\n", szam, min, max); return 0; } Paraméterek átadása tömbök tömbök függvényeknek való átadásának két ekvivalens módja van tömb formában kezeljük őket típus t[] vagy típus m[][] pointer formában kezeljük őket típus *t vagy típus **m #include #include int atlag(int t[], int hossz) int atlag(int *t, int hossz) { { int i; int i; int s = 0; int s = 0; for(i = 0; i < hossz; i++) for(i = 0; i < hossz; i++) { { s += t[i]; s += *(t + i); } } return (double)s/hossz; return (double)s/hossz; } } int main() int main() { { int t[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} int t[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} printf("> a tomb elemeinek atlaga: %.2f\n", atlag(t, 10)); printf("> a tomb elemeinek atlaga: %.2f\n", atlag(t, 10)); return 0; return 0; } } Érték és mellékhatás ÉRTÉK a függvény értéke/a függvény MELLÉKHATÁS a függvény lefutásakor kiértékelése valahol változást okoz a függvény lefut és a hívás helyén levő ezt abban az esetben is mellékhatásnak kifejezésbe behelyettesítésre kerül annak nevezzük, hogyha ez volt a függvény visszatérési értéke kifejezett célja printf("> %d\n", fakt(6)); printf("> %d\n", rand()); kvíz 4. kérdés printf("> %d\n", fakt(6)); printf("> %d\n", rand()); printf("> %d\n", fakt(6)); printf("> %d\n", rand()); > 720 > 56383 > 720 > 123 > 720 > 6823420 általában igyekszünk olyan függvényeket írni amelyeknek csak értéke vagy csak mellékhatása van 1275 8726 eszerint kétféle függvény van: parancsfüggvény (command): azért használjuk, hogy hatása legyen lekérdező függvény (query): kérdéseket teszünk fel, kiszámol valamit, mellékhatása nincs command-query separation FIGYELEM ha ez a kettő keveredik, akkor könnyen átláthatatlan programot eredményez Visszatérés a függvényből a függvény törzsében elhelyezett return utasítással visszatérünk a függvény hívásának helyére, egy függvényben több ilyen pont is lehet ezzel egyben megadjuk a visszatérési értéket is, ezt még a függvény értékének is nevezzük: maximum egy érték return utasításonként FONTOS a return utasítás a fenti két szerepet elválaszthatatlanul összeköti FIGYELEM ami a return után van, az már nem kerül végrehajtásra, viszont egy függvényben több helyen is szerepelhet return utasítás az előre definiált exit() utasítás ezzel ellentétben azonnal kilép a program végrehajtásából a main() is egy függvény érdekesség egy egész számmal kell visszatérjen, amelynek hibajelző szerepe van egyelőre ezt mindig 0 értékre állítjuk, ami azt jelenti, hogy nincs hiba a main() paraméterei: egyelőre hagyjuk nyitva a kérdést, hogy mik lehetnek az ő paraméterei További részletek bibliográfia K. N. King C programming – A modern approach, 2nd edition, W. W. Norton & Co., 2008 9. és 10. fejezet Deitel & Deitel C How to Program, 6th edition, Pearson, 2009 5. fejezet kvíz 5. kérdés 1275 8726