A program részekre bontása



Ahogy a program kódjának mérete hízik, az ismétlődések elkerülése végett, illetve az átláthatóság miatt célszerű azt részekre bontani. A részekre bontásnak több szintje, van amin ez a témakör sorra halad át.

1, Blokk
    1a, A változók érvényességi tartománya
2, A függvény
    2a, A matematikai függvény
    2b, A matematikai példa kódja
    2c, A függvény programozási oldala
    2d, Az eljárások   
    2e, A cím/referencia szerinti paraméterátadás
    2f, A köbös példa eljárással
    2g, Rekurzív függvények
    2h, A függvénymutató
3, Makrók
    3a, A fordítói direktívák
        3a:1, #define
        3a:2, #undef
        3a:3, #ifdef, #endif
        3a:4, #ifndef, #endif
        3a:5, #else, #elif
        3a:6, #include
        3a:7, #pragma
    3b, A makró definiálása
4, Függvénytárak
    4a, Függvénytár készítése


1, Blokk

Mint ez már a korábbi témakörökből látható volt, olyan helyeken (pl. ciklusok), ahol csak egy utasítást lehetne lefuttatni, kapcsos zárójelek által többet is lehet. Ezt nevezzük blokknak.

A program írása során rengeteg helyen készíthetőek blokkok, akár másik blokkon belül is. Ez egyfajta hierarchiát ad majd a későbbiekben.

{
      {
         ...
      }

      ...
  
      {
         ...
      }
}

A blokkok a legtöbb szerkesztőben (pl. Notepad++) összecsukhatóak, az olvasás elősegítése érdekében. Ebből kiindulva készíthetünk olyan részeket - amik valójában nem blokkok de összecsukhatóak - ezek a régiók:

íme kinyitva:

//{ Ez egy régió.

      {
         ...
      }

      ...
  
      {
         ...
      }
//}

és öszecsukva:

//{
[...]

A régió tehát egy olyan blokk, ami kommenten belül van - kizárólag rendezési célokat szolgál.

1a, A változók érvényességi tartománya

Mint ez már az előző fejezetekből kiderült, a C/C++ nyelvben a változók közel tetszőleges helyen deklarálhatóak.

Szintén egy logikus észrevétel, hogy a változót nem használhatjuk deklarálás előtt (az bal oldali fog csak lefutni):

int i;
i = 5;
i = 5;
int i;

Ezt a logika blokkokra kiterjesztve azt jelenti, hogyha egy adott blokkon belül veszünk fel változót, az csakis az adott blokkban, illetve annak alblokkjaiban lesz használható. Íme:

{
   ...
   int
i;
   i = 5;
  
   {
     i = 6;
   }
}
{
   ...
   int
i;
   i =
5;
}

i = 6;

Ebből az következik hogy egy változó csakis addig létezik, ameddig abban a blokkban vagyunk (vagy alblokkjaiban), ahol deklaráltuk.

Ezen elv alapján felállítható két csoport a változókra:

  • globális változó: még a legelső blokk előtt van deklarálva, így minden blokkból elérhető - csak a program befejezésekor szűnik meg
  • lokális változó: egy adott blokkon belül van deklarálva, így csak azon belül (és alblokkjaiban) elérhető - akkor szűnik meg, ha véget ér az a blokk amiben deklarálták

További kérdés lehet hogy mi van akkor ha két azonos nevű változó áll külön szinteken:

{
   ...
   int
i;
   i = 5;
  
   {
     int i;
     i = 6;

     {
        i = 7;
     }

     ...
   }
}

Ez a kódrészlet is hibátlan:

  • a külső blokkban felveszünk egy i változót, és értékét átírjuk 5-re
  • a belső blokkban felveszünk egy i változót, és értékét átírjuk 6-ra (ez egy másik i változó - csak a neve ugyanaz)
  • az i = 7 utasítás a bentebbi i változóra vonatkozik (aminek 6 az értéke), az átírja

Ugyanakkor ha a bentebbi blokkból szeretnénk elérni a külsőben lévő i-t akkor kudarcot fogunk vallani. Ilyenkor érdemes más nevet adni a bentebbinek, pl. i_, stb...

{
   ...
   int
i;
   i = 5;
  
   {
     int i_;
     i_ = 6;

     {
        i = 7;
     }

     ...
   }
}

Így a legbelső blokkban lévő utasítás, a külső i változó értékét fogja átírni (5 -> 7).

2, A függvény

A függvény a definíciója alapján egy egyértelmű hozzárendelés, ahol egy adott értékcsoporthoz, egy másik értékcsoportot rendelünk.

2a, A matematikai függvény

A matematikában a legegyszerűbb függvény a valós függvény, ahol egy valós számhoz rendelünk, egy másik valós számot. Egy ilyen függvény megadható a képletével, illetve grafikonnal:

Ez azt jelenti hogy minden számhoz (amit x helyére kell beírni), kiszámolja a harmadik hatványát - és az lesz az eredménye.

A függvények számításokba való beágyazásokhoz is alkalmasak, pl.:

Az f(-2), azt jelenti hogy az x helyére (-2) értéket írunk. Ekkor a függvényt kiszámoljuk [ = (-2)*(-2)*(-2) = -8] és az eredményt beírjuk az f(-2) helyére.

2b, A matematikai példa kódja

A programozás során a függvények a sok változótípus miatt, gazdagabb be és kimenőadatokkal dolgozhatnak, így nem elég felírni a képletet egy blokkba - hanem azt fejléccel is kell ellátni. Íme egy függvénydeklaráció:

<eredmény típusa> <függvény neve>(<szükséges adatok>)
{
   ...
   <számítások elvégzése>
   ...

   return <eredmény>;
}

A fentebb leírt matematikai példa kódja tehát:

int f(int x)
{
   return x*x*x;
}


int i = 2 * (3 + f(-2)); //i == 10

2c, A függvény programozási oldala

Egy ilyen példa esetén elgondolkodik az ember hogy van-e értelme ilyen függvényeket készíteni, mikor lényegében alig számol valamit.

Természetesen a példánál néhány fontos dolog kimaradt:

  1. Általában egy függvénynek értelmesebb nevet szokás adni, (pl. int cube(int x) a fenti példához).

  2. A függvények habár nem ágyazhatóak egymásra, szintén bírnak érvényességi határokkal.

    Ha a fenti kódban az f(-2) még az előtt jelenne meg, mielőtt az f függvény kódja kidolgozásra került, akkor a program nem fordul le.

  3. A függvény meghívásakor nem csak konstans/változó adható át hanem teljes kifejezés is.

    A fejlécben az adatok listája nem összevonható, mint a változóké - mindig ki kell írni a típust és a nevet is.

    Tömbök, mátrixok, stb. általában mutatóként adhatóak át, ilyenkor meg kell adni a méreteiket is.

  4. Minden változó ami a függvényben fel volt véve eltűnik, hiszen a blokk véget ér.

    Ha többször hívjuk meg egymás után a függvényt, minden alkalommal teljesen új változók lesznek felvéve.

    A fentebbi okok miatt, ha meg akarunk bentről tartani valamit, az legyen visszatérési érték, vagy másoljuk ki globális változóba.

  5. Ha a return utasítás után, ha még lenne kód az már nem futna le, mivel a return befejezi a függvény futását, és visszadja az értéket.
  6. A program tartalmaz egy "int main()" függvényt is, mikor elindítjuk a programot először ez fut le. A main később függvényt egy külön cikk tárgyalja.

A függvények igazi előnye nem ilyenkor van, hanem mikor nagyon összetett számolási algoritmusunk van (pl. másodfokú egyenlet megoldása), ahol a megfelelő együtthatók megadása után a függvény visszaadja a lehetséges megoldásokat.

Megjegyzés: Mindig figyeljünk oda, hogy a függvényünknek legyen visszatérési értéke - még akkor is ha hiba történik. Így elkerülhetőek a lánchibák (ha máshol a rossz eredménnyel számolunk tovább az lánchibát eredményez).

Természetesen nem csak számításokat tud elvégezni egy függvény, hanem bizonyos esetekben feladatok végez el - pl. szöveg kiírása a képernyőre, vagy fájlokkal kapcsolatos műveletek elvégzése. Ilyenkor nem mindig fontos a visszatérési érték, sőt sok esetben nincs is rá szükség, ilyenkor eljárásokról beszélhetünk.

2d, Az eljárások

Mint minden függvénynek kell visszatérési érték, itt is lesz - ám az eljárás egy olyan speciális függvény, ahol a visszatérési érték void lesz. Ezek után a return már nem használható a fenti alakban - ennek ellenére mégis ki lehet vele lépni az eljárásból:

void eljaras()
{
   return;
}

Az eljárás nem ágyazható be kifejezésbe, csak direkt módon hívható meg:

eljaras();

Az eljárások is bekérhetnek adatokat (paramétereket) mint a függvények - ám mivel nincs visszatérési értékük, hogyha eredményt szeretnének visszadni egy kissé más lesz a helyzet mint eddig:

2e, A cím szerinti paraméterátadás

A paraméterátadásnak két módja van:

  • érték szerinti paraméterátadás: a függvény/eljárás adatokat kér be, amik lehetnek konstansok vagy változók (eddig ez volt a példákban)
  • cím szerinti paraméterátadás: a függvény/eljárás egy memóriacímet kér be, ami csak változó címe lehet
  • konstans paraméterátadás: csak olvasásra szól a paraméter
  • referencia szerinti paraméterátadás [csak C++]

A kettő átadás jelentős különbséggel bír - viszont a különbség megértéséhez jobban meg kell vizsgálni a függvény adatbekérési módját.

Mikor a függvény adatot kér be, a fejlécben lényegében változódeklaráció történik, és a a program belemásolja a megadott értéket a fejlécben lévő változókba.

1,

Érték szerinti paraméterátadásnál az adott konstans/változó értéke kerül bemásolásra.

Ennek eredményeként, az szerkeszthető, de mikor a függvény befejezi futását, mint ez fentebb le lett írva, minden változója elvész (kivéve vissz. érték/glob. vált.-ba másolt adat), így ezek is.

Lényegében tehát ha a függvényes példában kiadnánk egy (x = 6) sort, akkor az csak a függvényen belüli x változót írná át, a külső értéket, amit átadtunk azt nem.

2,

Cím szerinti paraméterátadásnál az adott változó memóriacíme kerül bemásolásra.

Mivel ez egy mutató lesz, a változó tartalma átírhatóvá válik, következésképpen a függvényből/eljárásból több adat is kivihető mint amikről eddig szó esett.

Ez a mód teljesítménybeli növekedést is eredményezhet, ha pl. nagy méretű struktúrákkal van dolgunk - hiszen nem fogja a gép minden mezőjét átmásolni, hanem közvetlenül szerkeszthetővé válik a struktúra.

Ugyanakkor ez a mód veszélyekkel járhat, mint minden mutatóművelet. Bármikor lehetőség nyílik arra hogy az adat helyére érvénytelen mutatót adjanak meg - és ezzel számolni kell a függvény futása során - legalább a NULL esetére.

A csak C++ alatt elérhető referencia szerinti megoldás ugyanezt végzi el biztonságosan - nem lehet az értéke NULL, és mindig érvényes változó lesz írásra használva, valamint nem kell a mutatóoperátorokat használni.

A konstans paraméterátadás szintén egy automatikus rendszer, hasonló a referenciához, de az adott változó csak olvasható lesz - ha pedig konstans adatot adtunk meg, akkor azt bemásolja. Ez még működik C/C++ alatt is.

2f, A köbös példa eljárással

Íme tehát a fentebbi köbös példa cím szerinti paraméterátadással:

void cube(int ertek, int* eredmeny)
{
   (*eredmeny) = ertek * ertek * ertek;
}


int kob;
cube(-2, &kob); //kob == -8


int
kob = -2;
cube(kob, &kob); //kob == -8


cube(-2, NULL);

/* a program összeomlik - érvénytelen memóriacímre próbál majd írni a cube függvény */

Így működik tehát a cím szerinti paraméterátadás.

Ugyanakkor a példa összeomlik a NULL, vagy egyéb érvénytelen mutató hatására - ezt le lehet ellenőrizni:

void cube(int ertek, int* eredmeny)
{
   if (eredmeny != NULL)
     (*eredmeny) = ertek * ertek * ertek;
   else
     return;
}


cube(-2, NULL);

/* most már nem csinál semmit - össze sem omlik, de nem is számol */

 


Ha C++ alatt dolgozunk, és szintén célunk a visszaírás egy változóba, érdemes referencia szerinti paraméterátadással dolgozni:

void cube(int ertek, int& eredmeny)
{
   eredmeny = ertek * ertek * ertek;
}


int kob;
cube(-2, kob); //kob == -8


int
kob = -2;
cube(kob, kob); //kob == -8

Ennél a módszernél nem lehet NULL értéket átadni, sem érvénytelen adatot - a fordító megköveteli egy megfelelő változó megadását.


C++ alatt továbbá lehetőségünk van arra is hogy a függvény által bekért adatoknak alapértelmezett érteket adjunk, a vége felől elindulva visszafele (természetesen cím/paraméter szerinti megoldásnál nem).

Ez előnyös akkor ha sokféle függvényhívást szeretnénk biztosítani:

void teglalapot_rajzol(int x0, int y0, int x1, int y1, int rx = 0, int ry = 0)
{
   ...
}


teglalapot_rajzol(0, 0, 10, 10, 5, 5);


teglalapot_rajzol(0, 0, 10, 10);

Természetesen ilyenkor a kezdőértékkel rendelkező adatok elhagyhatóak.


A program gyorsítása végett lehetőségünk van arra is hogy a változókat ne másoltassuk át a géppel, mégis csak olvasásra kérjük - ez lesz a konstans átadás:

void teglalapot_rajzol(const int x0, const int y0, const int x1, const int y1, const int rx = 0, const int ry = 0)
{
   ...
}


teglalapot_rajzol(0, 0, 10, 10, 5, 5);


teglalapot_rajzol(0, 0, 10, 10);

Ezáltal nagy struktúrák is átadhatóak paraméterként, gyorsan, másolás nélkül, biztonságosan, C/C++ alatt is.

2g, Rekurzív függvények

Sok esetben előfordulhat az is, hogy olyan függvényre van szükségünk amely ismétlődően csinálja ugyanazt - pl. a faktoriális számításához.

1! = 1

2! = 2 * 1

3! = 3 * 2 * 1

...

10! = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1

...

n! = n * (n-1) * ... * 2 * 1

Ilyenkor ha kíváncsiak vagyunk a 25-re, azt csak úgy érhetjük el ha összeszorzunk előtte 24-et. Ilyenkor jön képbe a rekurzió.

Rekurzív függvény alatt olyan függvényt értünk, ami önmagának ismétlődő meghívásával végez feladatok/számol ki valamit.

A rekurzív függvény hogy mindig mást tudjon csinálni, a paramétereinek változtatásával érheti el a megfelelő feladat elvégzését:

int faktorialis(int i)
{
  if (i <= 1)
    return 1;
  else
    return i * faktorialis(i - 1);
}


int i = faktorialis(5);

Természetesen mindig kell kilépés a függvényből - ennek híján a programunk lefagy/összeomlik - itt most akkor lesz kilépés ha (i <= 1).

Íme a hívási sorrend:

faktorialis(5);
  faktorialis(4);
    faktorialis(3);
      faktorialis(2);
        faktorialis(1);

És mivel az utolsó visszatérési értéke 1 lesz (mert i <= 1), a következő visszatérési értéke i * 1 (= 2 * 1 = 2), a következőé aki az előzőekre várt i * 2  (= 3 * 2 = 6), és így tovább ahol az utolsóé 5 * 24 = 120.

Miután minden függvény kilépett az i értéke 120 lesz.

Kilépési pont híján viszont a sorrend nem fog visszafordulni egy ponton sem, csak nyitja meg az újabbnál újabb függvényeket, ameddig hagyja a gép - aztán összeomlik a program.

Tipp: Mivel a rekurzív függvények átgondolása komoly felkészültséget igényel, érdemes eltöprengeni azon hogy a példa nem valósítható-e meg ciklusok által, pl.:

int faktorialis(int n)
{
  int i, eredmeny = 1;

  for (i = 1; i <= n; i++)
    eredmeny *= i;

  return eredmeny;
}


int i = faktorialis(5);

Az eredmény ugyanaz, de most talán átláthatóbb megoldással, rekurzió nélkül.

2h, A függvénymutató

A deklarálásáról már szó esett a változók bemutatásánál, de sem a szerepéről, sem a működéséről nem esett szó. Megértéséhez két dolog ismerete szükséges:

  1. Mi az a függvény? - lásd fentebb
  2. A program egy adathalmaz - ebből kifolyólag miután betöltődött a memóriába, az egyes függvények is kapnak memóriacímet. Ez a cím lehetővé teszi hogy név nélkül futtassuk a függvényt - ám ez még több veszélyt hordoz mint egy rossz változó - rossz cím/fejléc esetén hibákat okozhat a program.

A függvények memóriacímét szintén az & jel segítségével kérhetjük le mint bármely változóét, de a típus különleges lesz. Íme:

typedef float (*TFuggveny)(float);

Ennek értelmezését úgy kell elvégezni, mint bármelyik függvény fejlécét - most azzal a plusszal hogy a függvény neve előtt egy * jel jelöli hogy most mutatóról van szó (ez lesz a típus neve különben), - illetve a bekérendő adatoknak nem adunk nevet.

Innentől kezdve bármely olyan függvény ami megfelel ennek a mintázatnak, futtatható név nélkül ilyen TFuggveny típusú változóból.

Ez megfelel a mintázatnak:
float parabola(float x)
{
   return x*x;
}

 

Ez nem felel meg a mintázatnak, habár a függvény ettől még helyes:
int cube(int z)
{
   return z*z;
}

Ha van egy mintázatnak megfelelő függvényünk akkor így használhatjuk név nélkül:

TFuggveny f = &parabola;
int i = f(5); /* ugyanaz mint a parabola(5) */

Ennek akkor lehet értelme ha egy program rugalmasságát akarjuk növelni - pl. egy for ciklus adja össze az első 10 számot, vagy azok négyzetét, vagy azok köbét. Íme a kód:

float x(float x)
{
   return
x;
}

float x2(float x)
{
   return x*x;
}

float x3(float x)
{
   return x*x*x;
}


int
main()
{
   int
i = 1, j, sum = 0;
   TFuggveny
fv;

   switch (i)
   {
      case 1:
        fv = &x;
        break;
      case 2:
        fv = &x2;
        break;
      case 3:
        fv = &x3;
        break;
   }

   for (j = 1; j <= 10; j++)
     sum += fv(j);

   return 0;
}

A program azért érdekes, mert nem tudja a for ciklus hogy most épp melyik függvényt használja - ennek ellenére az i értékének változatásával (1..3), megadhatjuk hogy melyik függvényt használja a szummázás során. Ezt a mutató általi függvényhívást, a függvények indirekt hívásának nevezzük.

Itt pedig egy példát is láthattunk a main() függvényre - ha ez az első fejezet ahol programot kezdtél írni akkor lehet hogy újdonság, ennek ellenére remélhetőleg aki eddig bemagolta hogy kell, az most már érti is hogy ez micsoda is.

3, Makrók

Ha olyan összedobott függvény írása a cél, mint a cikk elején lévő f(x), akkor érdemes makrókban is gondolkozni. A makrók előállításához viszont először meg kell ismerkedni az fordítói jelzésekkel/direktívákkal.

3a, A fordítói direktívák

Mivel a fordító számára jeleznek elő dolgokat, ezért a kód felett állnak ezek a jelölések. A használatukkal meg lehet adni hogy mi legyen benne a kódban, és mi nem - illetve hogy néhány azonosítót milyen más adatra cseréljen ki a fordító.

3a:1, #define <név> [érték]

Névvel ellátott konstansok definiálásához használatos. Lényegében ennek hatására a fordító az összes <név> azonosítót kicseréli az itt definiált értékre. pl.:

#define M_PI 3.1416

int i = 2 * M_PI;

Az érték akár el is maradhat - ekkor logikai igaz értéket definiáltunk.

3a:2, #undef <név>

Törli a már definíált  <név> nevű konstansot.

3a:3: #ifdef <név> / #endif

A két kulcsszó között lévő kódot csak akkor veszi figyelembe a fordító, ha az <név> értékkel már definiálva van konstans.

#define ALLITAS
#ifdef ALLITAS

int i = 2;

#endif

3a:4: #ifndef <név> / #endif

A kód csak akkor lesz belefordítva ha még nincs a <név> értékkel definiált konstans.

#ifndef UZENET
 
#define UZENET "Teszt üzenet"
#endif

3a:5: #else / #elif <név>

Az if szerkezet else - else if () ágát jelöli:

#ifndef UZENET
 
#define UZENET "Teszt üzenet"
#else
  #define UZ_2 "Második üzenet"
#endif

3a:6: #include <<fájlnév>>

Egy már meglévő fájl belemásolását kéri a programba.

#include <stdio.h>

3a:7: #pragma <információ>

A fordítóval közölhetünk különféle információkat - fordítónként változik. pl. GCC esetén:

#pragma GCC warning "Ez egy figyelmeztetés!"

3b, A makró definiálása

A makró definiáláshoz csak a #define kulcsszóra lesz szükség - ám ezúttal konstans helyett utasításokat fogunk begépelni:

#define cube(x) (x*x*x)


int i = 2 * (3 + cube(-2)); //i == 10

A makró csak egyszerű operátorból állhat, rövid utasítássorozatból:

#define max(x, y) (x > y ? x : y)


int i = max(4, 5);

A makróknál nincs típusosság mint a függvénynél - ha szöveggé szeretnénk alakítani pl. valamit akkor a # operátor lesz a segítsünkre - a több sorba tördelést pedig a "\" jel teszi lehetővé:

#define textout(a) \
   puts("Ez egy szöveg: " #a "\n")


textout(3); //kiíratás: "Ez egy szöveg: 3"

Ha egy változót szeretnénk elérni akkor a ## operátorral tudjuk megtenni:

#define intout(a) \
   printf("i" #a "= %d\n", i##a);


int i1 = 17, i2 = 23, i3 = 29;
intout(
1);  //kiíratás: "i1 = 17"
intout(
2);  //kiíratás: "i2 = 23"
intout(
3);  //kiíratás: "i3 = 29"

4, Függvénytárak

A függvénytáraknak nagy szerepe van a C/C++ nyelvben. Tegyük föl hogy készítettünk egy csomó, sok kódból álló függvényt, és szeretnénk több programban is használni - ilyenkor kell a függvénytár.

Függvénytárakból sok féle variáció van, a leggyakoribb három:

  1. kódot tartalmazó függvénytár (.H + .C)
    • csak egy adott programnyelven működik
    • mivel kódot tartalmaz, azt a kódot bárki szerkesztheti
    • egy javított változat használatához újra kell fordítani a programot
    • egy modult képez a programmal
    • nagyon kicsi, könnyen terjeszthető
  2. statikus csatolás függvénytár (.LIB)
    • sokféle programnyelven működhet
    • a kódot lefordítva tartalmazza, azt nem szerkeszthetik
    • egy javított változat használatához újra kell fordítani a programot
    • egy modult képez a programmal
  3. dinamikus csatolású függvénytár (.DLL)
    • szinte minden programnyelven működik
    • a kódot lefordítva tartalmazza, azt nem szerkeszthetik
    • egy javított változat használatához csak ki kell cserélni a fájlt, a programot nem kell újrafordítani
    • a program memóriájába töltődik, mégis külön modul
    • bármikor megnyitható/lezárható, akár függvényhívásonként (ezért dinamikus csatolású)
    • tartalmazhat erőforrásokat is (képek, zenék, ...)
    • a "C:\Windows\System32\" mappa rengeteg ilyet tartalmaz
    • a függvények nevét és címét tartalmazza, de paraméterezésüket nem, ezáltal szükséges lehet egy header (.H) fájl hozzá pl. winioctl.h, windows.h, ...
    • mivel egyetlen fájl, könnyen terjeszthető

Mivel az első variáció elkészítése a többihez képest lényegesen könnyebb, ezért ez a fejezet csak a kódot tartalmazó függvénytárakkal foglalkozik, és mostantól a függvénytár szó alatt ezeket érti.

A C nyelvben néhány gyakori függvénytár:

  • stdio.h - szöveges módú parancsok, kiíratás, beolvasás, fájlkezelés
  • stdlib.h - kiegészítő parancsok, pl. véletlenszám-generálás, memóriafoglalás calloc segítségével, stb.
  • math.h - matematikai függvények, pl. gyökvonás, hatványozás, sin, cos, ...
  • string.h - szövegekkel kapcsolatos függvények, összehasonlítás, összeadás, stb.
  • limits.h - az egyes változótípusok korlátait tároló konstansokat tartalmaz
  • time.h - dátum/idővel kapcsolatos utasításokat, típusokat tartalmaz

Ezek beszerkesztését az #include segítségével tehetjük meg.

4a, Függvénytár készítése

  1. Készítsünk egy .C fájlt, ami tartalmazza a függvényeinket
  2. Készítsünk egy .H fájlt, amibe bemásoljuk azon függvények fejléceit, amit a másik programrészből is szeretnénk elérhetővé tenni. Ha makrókat / globális változókat / konstansokat / típusdefiníciókat / stb. is szeretnénk közössé tenni az is ide kerüljön, ha nem akkor a .C fájlba.
  3. Készítsünk védelmet #ifndef, #define, #endif kulcssszavakkal a .H fájlba, arra az esetre ha valaki többször is beszeretné szerkeszteni az #include parancssal
  4. A .C fájlunkba szerkesszük be a .H fájl az #include paranccsal (ilyenkor mivel a fájl a sajátunk idézőjelek kellenek)
  5. A két fájlt együttes terjesztéssel nyomhatjuk is fel a netre. Annyi hogyha használni szeretnénk, akkor a legtöbb IDE alatt egy projektbe kell tenni a főprogrammal (pl. Code::Blocks).

És akkor íme egy példa függvénytár:

TESZT.C

#include "TESZT.H"

int Square(int x)
{
  
return x * x;
}

int Cube(int x)
{
  
return x * x * x;
}

int Power(int x, int n)
{
  
if (n < 1)
    
return 1;
  
else
    
return x * Power(x, n - 1);
}

TESZT.H

#ifndef TESZT_H_
  #define TESZT_H_

  int Square(int x);
 
int Cube(int x);
  int Power(int x, int n);

#endif

A főprogram fájlja (pl. MAIN.C):

#include "TESZT.H"

int main()
{
   int i = Power(Square(5) - Cube(2), 4);
 
  
return 0;
}

Megjegyzés:
  C++ alatt léteznek névterek is, pl. std:: - amelyek több függvénytárból is állhatnak. Ez lényegében nem több mint egy csoportosítási elem.
  Ha nem akarjuk mindig kiírni az std:: kezdést a névtérnél - használjuk az alábbi sort:
   
 using namespace std;
  Ha ez lemarad és ennek ellenére elhagyjuk az
std:: kezdést, akkor az adott névtérbeli elemek nem lesznek elérhetőek, a program nem lesz lefordítható.