A FELHASZNÁLÓ ÁLTAL DEFINIÁLT TÍPUSOK
9. FÜGGVÉNYEK 161 6 A return utasítás az eredmény értékét ( Rvalue ) téríti vissza Ezért
9.4. PROGRAMTERVEZÉS KICSIBEN
int i, j; FILE * f;
f = fopen("matrix.be", "r"); fscanf(f, "%d%d", &n, &m);
for(i = 0; i < n; i++) for(j = 0; j < m; j++) fscanf(f, "%d", &a[i][j]); fclose(f); } void kiiras() { int i, j; FILE * f; f = fopen("matrix.ki", "w"); for(i = 0; i < n; i++) { for(j = 0; j < m; j++) fprintf(f, "%5d", a[i][j]); fprintf(f, "\n"); } fclose(f); } void sor_rendezes(int k) { int i, j; for(i = 0; i < m - 1; i++) for(j = i + 1; j < m; j++)
if (a[k][i] > a[k][j]) csere(&a[k][i], &a[k][j]); }
void csere(int * p1, int * p2)
{
int v;
v = *p1; *p1 = *p2; *p2 = v; }
A fenti programtervezés indoklása
1. Mivel a függvények globális változókon keresztül jutnak hozzá n- hez,m-hez ésa-hoz, ezért kevésbé általánosak. Például abeolvasas
függvény nem lenne alkalmas egy mátrix adatainak p (sor), q (os- zlop) ésb (elemek) változókba való beolvasására. De ebben a fela- datban erre nincs is szükség.
2. A fentiekkel ellentétben, mivel a sor_rendezes paraméteren keresztül kapja meg a rendezendő sor indexét, ezért az a tömb bármelyik sorának rendezésére alkalmas, és erre szükség is van. 3. Szükségtelen lett volna a sorindexet cím szerint átadni a
Ezzel ellentétben a csere függvénynek magukra az elemekre van szüksége a felcserélésükhöz.
4. Ha kényelemből (hibásan) globálisnak definiáltuk volna az i és j
változókat, hogy ne kelljen minden függvényben külön deklaráljuk, akkor a main függvény i ciklusváltozóját „elrontotta” volna a
sor_rendezes függvény.
5. Következésképpen, bár a globális változók használata kényelmes- nek tűnik, általában kerülendők. Célszerű előnybe helyezni a paraméterátadást és a lokális változókat. Sőt, egyes pedagógusok azt ajánlják a programozni tanulóknak, hogy teljességgel kerüljék a globális változók használatát. Ez egészséges programozói stílus kialakításához vezet, amely többek között azt is magába foglalja, hogy olyan függvényeket írunk, amelyek bármely alkalmazásban értékesí- thetők. Továbbá felkészít nagy projektek „csapatmunkával” való meg- valósítására. A programozói csapatmunka egyik alapelve, hogy a pro- jekt függvényei kizárólag paraméterlistán keresztül kommunikálnak, és csak lokális változókat használnak.
Következzék hát a feladat megoldása globális változók nélkül:
#include <stdio.h>
void beolvasas(int *, int *, int (*)[50], char *);
void kiiras(int, int, int (*)[50], char *);
void sor_rendezes(int, int, int [][50]);
void csere(int *, int *); main()
{
int n, m, a[50][50], i;
beolvasas(&n, &m, a, "matrix.be");
for(i = 0; i < n; i++) sor_rendezes(i, m, a); kiiras(n, m, a, "matrix.ki");
return 0; }
void beolvasas(int* pn,int* pm,int(*a)[50],char*allomany)
{ int i, j; FILE * f; f = fopen(allomany, "r"); fscanf(f, "%d%d", pn, pm); for(i = 0; i < *pn; i++) for(j = 0; j < *pm; j++) fscanf(f, "%d", &a[i][j]); fclose(f);
9.4. PROGRAMTERVEZÉS KICSIBEN 175
}
void kiiras(int n, int m, int (*a)[50], char * allomany) { int i, j; FILE * f; f = fopen(allomany, "w"); for(i = 0; i < n; i++) { for(j = 0; j < m; j++) fprintf(f, "%5d", a[i][j]); fprintf(f, "\n"); } fclose(f); }
void sor_rendezes(int k, int m, int a[][50]) {
int i, j;
for(i = 0; i < m - 1; i++)
for(j = i + 1; j < m; j++)
if (a[k][i] > a[k][j]) csere(&a[k][i], &a[k][j]); }
void csere(int * p1, int * p2) {
int v;
v = *p1; *p1 = *p2; *p2 = v; }
Megjegyzések ezen második megoldáshoz:
1. A beolvasas függvény most már alkalmas lenne egy mátrix adatainak a beolvasására, bármilyen nevű állományból bármilyen nevű memóriaváltozókba. Ugyanez igaz a kiiraséssor_rendezés
függvényekre is.
2. Abeolvasasfüggvénynek nyilván cím szerint kellett átadnunk azn,
mésaváltozókat, hiszen a beolvasásnak ezekbe kellett megtörténnie és nem a másolatukba. Azért nem kellett külön jelezni az a tömb cím szerint való átadását, mert, amint említettük, a C nyelvben a tömbök kivételesen implicite cím szerint adódnak át. A kiiras
és sor_rendezes függvények esetében elégséges volt n és m érték szerinti átadása.
3. Úgy is mondhatnánk, hogy a main függvény a saját n-jét, m-jét ésa-ját bocsátja a cím szerinti paraméterátadás által a beolvasas
függvény rendelkezésére, míg a másik kettőnek csupánnésmértékét adja oda.
Ha dinamikusan szeretnénk helyet foglalni egy tömbnek, ezt a felada- tot is kioszthatjuk egy függvénynek.
Aletrehoz_1egynelemű egydimenziósinttípusú tömböt hoz létre.
int * letrehoz_1(int n) { int * tp; tp = (int*)malloc(n * sizeof(int)); return tp; }
Hogyan használható a fenti függvény?
int * a;
a = letrehoz_1(n); /*a tömbi-edik elemére való hivatkozás:
a[i] */
A letrehoz_2 n x m-es float típusú tömböknek foglal helyet. Pon- tosabban, egy n elemű float-pointer tömböt hoz létre, amelynek minden eleme egy m eleműfloat-tömbre mutat.
float ** letrehoz_2(int n, int m) {
float ** tpp; int i;
tpp = (float**)malloc(n * sizeof(float*)); for(i = 0; i < n; i++)
tpp[i] = (float*)malloc(m * sizeof(float)) return tpp;
}
Kétdimenziós tömb létrehozása aletrehoz_2 segítségével:
float ** b;
b = letrehoz_2(n, m); /* b[i][j], a tömb i-edik sorának
j-edik eleme*/
Amíg aza tömb törölhető afree(a); utasítással, addig b törlését az alábbi függvény valósíja meg:
void torol_2(float ** b, int n) { int i;
for(i = 0; i < n; i++) free(b[i]) free(b);}
9.5. FÜGGVÉNYPOINTEREK 177
9.5. Függvénypointerek
Egy függvény típusán eddig a visszatérési értékének típusát értettük. Azonban, ha egészen pontosak akarunk lenni, akkor ez magába foglalja még a paraméterek számát és típusát is.
Legyen a következő függvénydeklaráció:
int f(char, float);
fegy char ésfloatparaméterű int-függvény. f típusa leírva:
int (char, float).
Hogyan definiálunk egy függvénypointert? Például így:
int (*fp)(char, float);
fp, egycharésfloatparaméterűint-függvénypointer.fptípusa leír- va:int(*)(char,float).
typedef-et használva nevet adhatunk egy függvénytípusnak vagy függvénypointer-típusnak.
Példa:
typedef float FV_TIP (double, int**, char[]); typedef int (*FV_PTR_TIP) (int, int);
FV_TIP fuggv; /* függvénydeklaráció, prototípus */ FV_PTR_TIP f_ptr; /* függvénypointer-definiálás */
Egy függvény neve az illető függvénytípusú pointerkonstansként is felfogható, amely magára a függvényre mutat. Tehát f egy int(*) (char,float)típusú függvénypointer-konstansnak is tekinthető. Ezért az
f, illetve *fhivatkozások egyenértékűek.
Az fp pointer megkaphatja bármely char és int paraméterű int- függvény címét.
Példa:
fp = &f; ezzel ekvivalens:fp = f;
Az érem másik oldala az, hogy bármely függvénypointer felfogható annak a függvénynek a neveként, amelyre mutat. Így az fp és *fp hi-
vatkozások is egyenértékűek.
Az ffüggvényt a következő módokon lehet meghívni:
int x; char a; float b;
...x = f(a, b); ... /*klasszikus forma */
...x = (*fp)(a, b); ... /* pointeren keresztül*/
...x = (*f)(a, b); ... /* a nevén mint pointeren keresz- tül*/
Megjegyzés. Mivel egy függvény típusa – az adattípusokkal ellentét- ben – nem határozza meg kódjának memóriaméretét, ezért a függvény- pointerekre nem érvényesek a címaritmetika műveletei. Például egy füg- gvénypointerhez nem adható hozzá egy egész szám, vagy az azonos típusú függvénypointerek nem vonhatók ki egymásból.
9.6. Függvény paraméterként való átadása függvénynek
A függvényátadás mindig cím szerint történik.
9.4. feladat. Írjunk egy olyan rendező függvényt, amely rendezi egy tömb elemeit, függetlenül ezek típusától.
Megoldás: Mivel a rendezéshez szükséges összehasonlítás típusfüggő, ezért minden típusra írunk egy összehasonlító függvényt, amelyet majd átadunk a rendező függvénynek. A rendező függvény paraméterként a tömb címét, elemeinek számát és méretét, valamint az összehasonlító függvényt fogja megkapni. Azért, hogy a különböző összehasonlító függvények azonos típusúak legyenek, az összehasonlítandó elemek címét mint const void
pointereket fogják megkapni.
#include<sdtlib.h>
#include<string.h>
int int_cmp(const void * p1, const void * p2) { return * (int *) p1 - * (int *) p2; }
int float_cmp(const void * p1, const void * p2) { if (* (float *) p1 == * (float *) p2) return 0;
else if (* (float *) p1 < * (float *) p2) return -1;
else return 1;}
void sort(void*v,int nr,int size,int(*p_cmp)(const void*,const void*)) {
int i, j; void *pv, *pi, *pj;
pv = malloc(size); /* a veder szerepét fogja betölteni */
for(i = 0, pi=v; i < nr - 1; i++, pi=(char*)pi+size) {
for(j = i + 1,pj=(char*)pi+size; j < nr; j++,pj=(char*)pj+size) {
if (p_cmp(pi, pj) > 0) {
memcpy(pv, pi, size); memcpy(pi, pj, size);
9.7. VÁLTOZÓ PARAMÉTERSZÁMÚ FÜGGVÉNYEK 179