A „C: a programozási nyelv alapjainak magyarázata” című cikkünkben mélyrehatóan bemutatjuk a C programozási nyelv alapvető koncepcióit, felépítését és gyakorlati alkalmazásait. A C egy rendkívül befolyásos és időtálló nyelv, amely a modern számítástechnika számos területének alapját képezi. Bár a technológia folyamatosan fejlődik, a C nyelvtudás továbbra is kulcsfontosságú számos speciális területen, és kiváló alapot biztosít más programozási nyelvek megértéséhez.
Bevezetés a C programozási nyelvbe: Miért érdemes ma is tanulni?
A C programozási nyelv egyike a legbefolyásosabb és legszélesebb körben használt nyelveknek a számítástechnika történetében. Dennis Ritchie fejlesztette ki a Bell Labsnál az 1970-es évek elején, azzal a céllal, hogy operációs rendszereket írjanak vele, különösen az UNIX-ot. Azóta a C hatalmas utat tett meg, és számos más programozási nyelv alapjául szolgált, beleértve a C++, Java, C#, JavaScript, és Python nyelveket is, amelyek szintaxisukban vagy alapvető koncepcióikban merítenek a C-ből.
Rövid történeti áttekintés
A C nyelv gyökerei a BCPL (Basic Combined Programming Language) és a B nyelvhez nyúlnak vissza. Ken Thompson a B nyelvet fejlesztette ki az első UNIX rendszerekhez. Dennis Ritchie továbbfejlesztette a B nyelvet, hozzáadva a típusrendszert, a struktúrákat, és számos más funkciót, létrehozva ezzel a C nyelvet. Az 1978-ban megjelent „The C Programming Language” című könyv, amelyet Brian Kernighan és Dennis Ritchie írt (gyakran K&R C-ként emlegetik), hosszú ideig a nyelv de facto szabványaként szolgált, mielőtt az ANSI C szabvány megjelent volna 1989-ben (ANSI X3.159-1989). Ezt követte a C99, C11 és C17/C18 szabványok, amelyek új funkciókkal bővítették a nyelvet, miközben megőrizték annak alapvető jellegét.
A C jellemzői: hatékonyság, hordozhatóság, alacsony szintű hozzáférés
A C számos olyan tulajdonsággal rendelkezik, amelyek kiemelik más nyelvek közül:
- Hatékonyság: A C rendkívül „közel áll a hardverhez”. Lehetővé teszi a programozó számára, hogy közvetlenül manipulálja a memóriát mutatók segítségével, ami rendkívül optimalizált és gyors kódot eredményez. Ez teszi ideálissá olyan alkalmazásokhoz, ahol a teljesítmény kritikus.
- Hordozhatóság: Bár a C alacsony szintű nyelv, a fordítók széles körben elérhetők különböző architektúrákon és operációs rendszereken. Ez azt jelenti, hogy egy jól megírt C program minimális módosítással vagy anélkül futtatható különböző platformokon.
- Alacsony szintű hozzáférés: A C lehetővé teszi a hardverrel való közvetlen interakciót, ami elengedhetetlen az operációs rendszerek, eszközmeghajtók és beágyazott rendszerek fejlesztéséhez.
- Modularitás: A függvények és fejlécfájlok használata révén a C támogatja a moduláris programozást, ami megkönnyíti a nagy projektek kezelését és a kód újrafelhasználását.
- Gazdag standard könyvtár: Bár a C maga viszonylag minimalista, a standard könyvtára számos hasznos funkciót biztosít, például fájlkezelést, sztringmanipulációt és matematikai műveleteket.
A C nyelv ereje abban rejlik, hogy egyensúlyt teremt az alacsony szintű hardverhozzáférés és a magas szintű programozási absztrakciók között. Ez teszi rendkívül rugalmassá és hatékonnyá.
Alkalmazási területek
A C nyelv rendkívül sokoldalú, és számos területen domináns szerepet játszik:
- Operációs rendszerek: Az UNIX, Linux, Windows kerneljeinek jelentős része C-ben íródott.
- Beágyazott rendszerek: Mikrokontrollerek, IoT eszközök, autóipari rendszerek, háztartási gépek firmware-jei gyakran C-ben készülnek a memóriahatékonyság és a közvetlen hardverhozzáférés miatt.
- Játékfejlesztés: Játék motorok (pl. Unreal Engine, Unity bizonyos részei) és grafikus API-k (OpenGL, DirectX) C vagy C++ nyelven íródnak a maximális teljesítmény érdekében.
- Adatbázisok: Számos adatbázis-kezelő rendszer (pl. MySQL, PostgreSQL) C-ben vagy C++-ban íródott.
- Fordítóprogramok és értelmezők: A legtöbb fordítóprogram és értelmező, beleértve a C fordítókat is, C-ben vagy C++-ban készül.
- Rendszerprogramozás: Hálózati protokollok, fájlrendszerek, segédprogramok.
A C szerepe a modern szoftverfejlesztésben
Bár sok „magasabb szintű” nyelv létezik, a C továbbra is releváns. A C nyelvtudás segít megérteni, hogyan működik a számítógép a legalacsonyabb szinten. Ez a mélyebb megértés felbecsülhetetlen értékűvé teszi a C-t azok számára, akik rendszermérnökökké, beágyazott rendszerek fejlesztőivé vagy teljesítménykritikus alkalmazások specialistáivá szeretnének válni. A C emellett ideális nyelv az algoritmusok és adatszerkezetek tanulmányozásához, mivel a memóriakezelés és a mutatók használata révén világosabban láthatóvá válnak a mögöttes mechanizmusok.
A C nyelvtudás nem csak egy eszköz, hanem egyfajta gondolkodásmód is. Megtanítja a programozót, hogy optimalizáljon, gondolkodjon a memóriáról, és megértse a hardver és szoftver közötti kapcsolatot. Ez az alapvető tudás rendkívül hasznos más nyelvek elsajátításakor és komplex rendszerek tervezésekor.
A fejlesztői környezet beállítása: A C programozás első lépései
Mielőtt belekezdenénk a C programozásba, szükségünk van egy fejlesztői környezetre. Ez alapvetően két fő komponenst foglal magában: egy fordítóprogramot (compiler) és gyakran egy integrált fejlesztői környezetet (IDE) vagy egy szövegszerkesztőt.
Fordítóprogramok (Compilerek)
A C forráskódot (amit mi írunk) egy fordítóprogram alakítja át gépi kóddá, amelyet a számítógép közvetlenül végre tud hajtani. A legelterjedtebb C fordítóprogramok a következők:
- GCC (GNU Compiler Collection): Ez a legnépszerűbb nyílt forráskódú fordítóprogram, amely számos platformon elérhető (Linux, Windows – MinGW vagy Cygwin segítségével, macOS). A GCC nem csak C-t, hanem C++, Fortran, Ada és más nyelveket is támogat.
- Clang: Egy másik népszerű nyílt forráskódú fordító, amely a LLVM infrastruktúrára épül. Gyorsabb fordítási idővel és jobb hibaüzenetekkel rendelkezik, mint a GCC, és egyre népszerűbb, különösen macOS-en és iOS fejlesztésben.
- MSVC (Microsoft Visual C++): A Microsoft saját fordítóprogramja, amely a Visual Studio IDE része. Főleg Windows platformon használják.
Telepítésük platformonként eltérő lehet. Linuxon általában a csomagkezelővel telepíthető (pl. `sudo apt install build-essential` Debian/Ubuntu alapú rendszereken, ami a GCC-t és más alapvető fejlesztői eszközöket is telepíti). Windowson a MinGW vagy a Cygwin telepítése ajánlott a GCC használatához, vagy a Visual Studio telepítése az MSVC-hez.
Integrált fejlesztői környezetek (IDE-k)
Az IDE-k olyan szoftverek, amelyek egyetlen felületen integrálják a kódíráshoz, fordításhoz és hibakereséshez szükséges eszközöket. Kényelmesebbé és hatékonyabbá teszik a fejlesztési folyamatot.
- Code::Blocks: Egy ingyenes, nyílt forráskódú, platformfüggetlen IDE, amely támogatja a C, C++ és Fortran nyelveket. Beépített GCC fordítóval érkezik, így kezdők számára is könnyen beállítható.
- Visual Studio Code: Egy könnyű, de rendkívül funkcionális és bővíthető szövegszerkesztő a Microsofttól. Bár nem teljes értékű IDE önmagában, kiterjesztésekkel (pl. C/C++ kiterjesztés, Code Runner) teljes értékű C fejlesztői környezetté alakítható. Platformfüggetlen.
- Eclipse CDT (C/C++ Development Tooling): Egy robusztus, nyílt forráskódú IDE, főleg Java fejlesztéshez ismert, de a CDT kiterjesztéssel kiválóan alkalmas C/C++ fejlesztésre is. Komplexebb projektekhez ajánlott.
- Visual Studio: Egy teljes értékű IDE a Microsofttól, főleg Windows fejlesztéshez. Erőteljes hibakeresővel és számos más eszközzel rendelkezik.
Választhatunk egyszerű szövegszerkesztőket is (pl. Notepad++, Sublime Text, Vim, Emacs), és a parancssorból futtathatjuk a fordítót, de az IDE-k kényelmesebbek a kezdők számára.
Az első „Hello, World!” program magyarázata
Minden programozási nyelv tanulásának klasszikus első lépése a „Hello, World!” program megírása. C-ben ez így néz ki:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
Nézzük meg sorról sorra a magyarázatát:
#include <stdio.h>
: Ez egy előfeldolgozó direktíva. Astdio.h
(standard input-output header) egy fejlécfájl, amely a standard bemeneti és kimeneti függvények deklarációit tartalmazza, mint például aprintf()
. Az#include
utasítja a fordítót, hogy a fordítás előtt illessze be ennek a fájlnak a tartalmát a programba.int main() { ... }
: Ez a program fő függvénye. Minden C program végrehajtása innen indul.int
: Amain
függvény visszatérési típusa. Azt jelenti, hogy a függvény egy egész számot fog visszaadni az operációs rendszernek. A0
általában a sikeres végrehajtást jelzi.main
: A függvény neve. Ez egy speciális név, amit a fordító keres a program indításakor.()
: A zárójelek azt jelzik, hogy ez egy függvény. Itt nincsenek paraméterei.{ }
: A kapcsos zárójelek a függvény törzsét, azaz a benne lévő utasításokat határozzák meg.
printf("Hello, World!\n");
: Ez az utasítás kiírja a „Hello, World!” szöveget a konzolra.printf()
: Egy standard könyvtári függvény, amely formázott kimenetet ír a standard kimenetre (általában a konzolra)."Hello, World!\n"
: Ez a sztring literál, amit ki akarunk írni. A\n
egy speciális karakter (escape sequence), amely sortörést jelent, azaz a kurzor a következő sor elejére ugrik a kiírás után.;
: Minden utasításnak pontosvesszővel kell végződnie C-ben.
return 0;
: Ez az utasítás visszaadja a0
értéket amain
függvényből. Ahogy említettük, a0
általában a program sikeres befejezését jelzi.
A „Hello, World!” program alapvető elemei (
#include
,main()
függvény,printf()
,return
utasítás) minden C program gerincét képezik. Fontos megérteni a szerepüket a C programozás alapjainak elsajátításához.
Fordítási és futtatási folyamat
A C programok elkészítése és futtatása általában a következő lépésekből áll:
- Forráskód írása: Egy szövegszerkesztőben vagy IDE-ben megírjuk a C kódot, és elmentjük egy
.c
kiterjesztésű fájlba (pl.hello.c
). - Fordítás: A fordítóprogram (pl. GCC) segítségével lefordítjuk a forráskódot futtatható (executable) fájllá. Parancssorból ez így néz ki:
gcc hello.c -o hello
Ez az utasítás lefordítja a
hello.c
fájlt, és létrehoz egyhello
nevű futtatható fájlt (Windows-onhello.exe
). - Futtatás: A létrehozott futtatható fájlt elindítjuk. Parancssorból:
./hello
Vagy Windows-on:
hello.exe
Eredményül a konzolon megjelenik a „Hello, World!” felirat.
Az IDE-k ezeket a lépéseket automatizálják, általában egy „Build” (fordítás) és „Run” (futtatás) gomb megnyomásával.
Alapvető adattípusok és változók: Az adatok tárolása C-ben
A programozás lényege az adatok kezelése. A C nyelvben az adatok különböző típusúak lehetnek, és ezeket változókban tároljuk. Egy változó alapvetően egy elnevezett memóriaterület, amely egy adott típusú értéket képes tárolni.
Egész számok
Az egész számok tárolására szolgáló adattípusok:
int
: A leggyakrabban használt egész típus. Mérete rendszerfüggő, de általában 4 bájt (32 bit), ami kb. -2 milliárdtól +2 milliárdig terjedő értékeket tud tárolni.short
: Kisebb méretű egész számokhoz, általában 2 bájt (16 bit), kb. -32 768-tól +32 767-ig.long
: Nagyobb egész számokhoz, gyakran 4 vagy 8 bájt (32 vagy 64 bit).long long
: A legnagyobb egész típus, garantáltan legalább 8 bájt (64 bit).unsigned
kulcsszó: Bármelyik egész típus elé írható, hogy csak pozitív értékeket tároljon, megduplázva ezzel a maximális tárolható értéket. Például:unsigned int
,unsigned long long
.
Példa deklarációra és inicializálásra:
int kor = 30;
unsigned int penz = 1000000;
short homerseklet = -5;
long tavolsag = 1234567890L; // L suffix a long literálhoz
long long nagySzam = 9876543210987654321LL; // LL suffix a long long literálhoz
Lebegőpontos számok
A tizedes számok (valós számok) tárolására szolgáló adattípusok:
float
: Egyszeres pontosságú lebegőpontos szám, általában 4 bájt.double
: Kétszeres pontosságú lebegőpontos szám, általában 8 bájt. Ez a leggyakrabban használt lebegőpontos típus a nagyobb pontosság miatt.long double
: Kiterjesztett pontosságú lebegőpontos szám, mérete rendszerfüggő, gyakran 10 vagy 16 bájt.
Példa:
float pi = 3.14F; // F suffix a float literálhoz
double ar = 19.99; // Alapértelmezetten double
long double hatalmasErtek = 1.234567890123456789L; // L suffix a long double literálhoz
Karakterek
Egyetlen karakter tárolására szolgál:
char
: Általában 1 bájt méretű. Képes tárolni egy ASCII vagy más karakterkódolású karaktert. Technikailag egy egész típus, így számértékeket is tárolhat (pl. -128-tól 127-ig vagy 0-tól 255-ig, attól függően, hogy `signed` vagy `unsigned char`).
Példa:
char betu = 'A';
char szamjegy = '7';
Logikai értékek
A C nyelv a C99 szabvány előtt nem rendelkezett beépített logikai (boolean) típussal. Hagyományosan az egész számokat használták logikai értékekként:
0
(nulla) hamisat (false) jelent.- Minden más, nem nulla érték igazat (true) jelent.
A C99 szabvány bevezette a _Bool
típust, amely a <stdbool.h>
fejlécfájlban bool
, true
és false
makrókként érhető el:
#include <stdbool.h>
bool igaz = true;
bool hamis = false;
Változók deklarálása és inicializálása
Mielőtt egy változót használnánk, deklarálnunk kell, azaz meg kell adnunk a típusát és a nevét. Inicializálhatjuk is, azaz kezdőértéket adhatunk neki a deklaráció pillanatában.
int szam; // Deklaráció
szam = 10; // Értékadás (inicializálás később)
double eredmeny = 3.14; // Deklaráció és inicializálás egy lépésben
A változóneveknek betűvel vagy aláhúzással kell kezdődniük, tartalmazhatnak számokat is, de szóközöket, speciális karaktereket (kivéve az aláhúzást) nem. A C kis- és nagybetűérzékeny (pl. szam
és Szam
két különböző változó).
A `sizeof` operátor
A sizeof
operátor visszaadja egy típus vagy egy változó méretét bájtokban. Nagyon hasznos a memóriakezelés megértéséhez és dinamikus memóriafoglalásnál.
#include <stdio.h>
int main() {
int a;
char c;
double d;
printf("Az int mérete: %zu bájt\n", sizeof(int));
printf("Az a változó mérete: %zu bájt\n", sizeof(a));
printf("A char mérete: %zu bájt\n", sizeof(char));
printf("A c változó mérete: %zu bájt\n", sizeof(c));
printf("A double mérete: %zu bájt\n", sizeof(double));
printf("A d változó mérete: %zu bájt\n", sizeof(d));
return 0;
}
A %zu
formátumjelölő a size_t
típusú értékek kiírására szolgál, ami a sizeof
operátor visszatérési típusa.
Típuskonverzió (explicit és implicit)
A C automatikusan végez típuskonverziót (implicit konverzió), amikor különböző típusú értékekkel dolgozunk egy kifejezésben, vagy amikor egy kisebb típust egy nagyobb típusba helyezünk. Pl. int
-ből double
-be konvertálás.
int a = 10;
double b = 3.5;
double eredmeny = a + b; // a (int) implicit módon double-lé konvertálódik
printf("Eredmény: %f\n", eredmeny); // Kimenet: 13.500000
Explicit konverziót (típus kényszerítést vagy „casting”-ot) is végezhetünk, ha egy típust kifejezetten egy másikba akarunk alakítani. Ez különösen fontos lehet, ha adatvesztést szeretnénk elkerülni, vagy éppen kontrollált adatvesztést akarunk elérni.
int osszeg = 10;
int darab = 3;
double atlag = (double)osszeg / darab; // osszeg explicit módon double-lé konvertálódik
printf("Átlag: %f\n", atlag); // Kimenet: 3.333333
// Adatvesztés példája
double nagySzam = 123.45;
int egeszResz = (int)nagySzam; // A tizedes rész elveszik
printf("Egész rész: %d\n", egeszResz); // Kimenet: 123
A típuskonverzió megértése kulcsfontosságú a pontos számításokhoz és a váratlan viselkedés elkerüléséhez.
Operátorok és kifejezések: Műveletek az adatokkal

Az operátorok olyan szimbólumok, amelyek műveleteket hajtanak végre egy vagy több operanduson (értéken vagy változón). A C számos operátort kínál, amelyek különböző típusú műveleteket tesznek lehetővé.
Aritmetikai operátorok
Matematikai műveletek végrehajtására szolgálnak:
+
: Összeadás-
: Kivonás*
: Szorzás/
: Osztás (egész számok esetén egész osztást végez, azaz a maradékot elhagyja)%
: Maradékos osztás (moduló operátor, csak egész számokra)
int a = 10, b = 3;
int osszeg = a + b; // 13
int kulonbseg = a - b; // 7
int szorzat = a * b; // 30
int hanyadosEgesz = a / b; // 3 (nem 3.33)
int maradek = a % b; // 1
Relációs operátorok
Két érték közötti kapcsolatot vizsgálják, és logikai (igaz/hamis) értéket adnak vissza (C-ben 1 az igaz, 0 a hamis):
==
: Egyenlő-e? (Vigyázat, nem összekeverendő az értékadó=
operátorral!)!=
: Nem egyenlő-e?<
: Kisebb-e?>
: Nagyobb-e?<=
: Kisebb vagy egyenlő-e?>=
: Nagyobb vagy egyenlő-e?
int x = 5, y = 10;
printf("%d\n", x == y); // 0 (hamis)
printf("%d\n", x != y); // 1 (igaz)
printf("%d\n", x < y); // 1 (igaz)
Logikai operátorok
Logikai értékeken (vagy kifejezéseken, amelyek logikai értékre értékelődnek ki) hajtanak végre műveleteket:
&&
: Logikai ÉS (AND). Igaz, ha mindkét operandus igaz.||
: Logikai VAGY (OR). Igaz, ha legalább az egyik operandus igaz.!
: Logikai NEM (NOT). Megfordítja az operandus logikai értékét.
int kor = 25;
int jovedelem = 50000;
// (kor > 18) && (jovedelem > 40000)
// Igaz, mert mindkét feltétel igaz
printf("%d\n", (kor > 18) && (jovedelem > 40000)); // 1
// (kor < 20) || (jovedelem > 60000)
// Hamis, mert mindkét feltétel hamis
printf("%d\n", (kor < 20) || (jovedelem > 60000)); // 0
// !(kor == 25)
// Hamis, mert a (kor == 25) igaz, és a ! megfordítja
printf("%d\n", !(kor == 25)); // 0
Fontos megjegyezni, hogy a logikai ÉS és VAGY operátorok "rövidzárlatos" kiértékelést végeznek. Ez azt jelenti, hogy ha az eredmény már az első operandusból eldönthető, a második operandust nem értékelik ki. Például A && B
esetén, ha A
hamis, B
-t már nem is nézik. Hasonlóan A || B
esetén, ha A
igaz, B
-t már nem is nézik.
Bitenkénti operátorok
Ezek az operátorok az operandusok bitjein hajtanak végre műveleteket. Rendszerprogramozásban, hardveres illesztőprogramokban és optimalizációknál nagyon hasznosak.
&
: Bitenkénti ÉS (AND)|
: Bitenkénti VAGY (OR)^
: Bitenkénti kizáró VAGY (XOR)~
: Bitenkénti NEGÁCIÓ (egyes komplementer)<<
: Bitenkénti balra eltolás>>
: Bitenkénti jobbra eltolás
unsigned int A = 5; // Binárisan: 00000101
unsigned int B = 3; // Binárisan: 00000011
printf("%u\n", A & B); // 1 (00000001)
printf("%u\n", A | B); // 7 (00000111)
printf("%u\n", A ^ B); // 6 (00000110)
printf("%u\n", ~A); // Negatív szám (pl. 4294967290, ha 32 bit unsigned int)
printf("%u\n", A << 1); // 10 (00001010) - balra tolva 1 bittel, szorzás 2-vel
printf("%u\n", A >> 1); // 2 (00000010) - jobbra tolva 1 bittel, osztás 2-vel
Értékadó operátorok
Értéket adnak egy változónak. Az alapvető az =
, de vannak kombinált értékadó operátorok is:
=
: Egyszerű értékadás+=
: Hozzáad és értékad (pl.x += y;
egyenlőx = x + y;
)-=
: Kivon és értékad*=
: Szoroz és értékad/=
: Oszt és értékad%=
: Maradékos oszt és értékad&=
,|=
,^=
,<<=
,>>=
: Hasonlóan a bitenkénti operátorokkal
int szamlalo = 0;
szamlalo += 5; // szamlalo most 5
szamlalo *= 2; // szamlalo most 10
Inkrementáló és dekrementáló operátorok
Egy változó értékét növelik vagy csökkentik eggyel:
++
: Inkrementálás (növelés eggyel)--
: Dekrementálás (csökkentés eggyel)
Két formájuk van:
- Előtag (prefix) forma:
++x
vagy--x
. Az érték növelése/csökkentése *először* történik meg, majd az új érték kerül felhasználásra a kifejezésben. - Utótag (postfix) forma:
x++
vagyx--
. Az érték *először* felhasználásra kerül a kifejezésben, majd *utána* történik meg a növelés/csökkentés.
int i = 5;
int j = ++i; // i most 6, j is 6
int k = 5;
int l = k++; // k most 6, l viszont 5
A feltételes (ternáris) operátor (?)
Ez egy egyedi operátor, amely három operandust vesz fel, és egy rövid, egysoros if-else
szerkezetet valósít meg:
feltétel ? kifejezés_ha_igaz : kifejezés_ha_hamis;
Példa:
int kor = 18;
char* statusz = (kor >= 18) ? "Felnott" : "Kiskoru";
printf("Státusz: %s\n", statusz); // Kimenet: Felnott
Precedencia és asszociativitás
Amikor több operátor szerepel egy kifejezésben, a precedencia (elsőbbség) határozza meg, milyen sorrendben értékelődnek ki. Például a szorzás és osztás magasabb precedenciával rendelkezik, mint az összeadás és kivonás. Zárójelekkel felülírhatjuk a precedencia szabályait.
Az asszociativitás határozza meg, hogy az azonos precedenciájú operátorok hogyan értékelődnek ki (balról jobbra vagy jobbról balra). A legtöbb operátor balról jobbra asszociatív, kivéve az értékadó és az unáris operátorokat, amelyek jobbról balra asszociatívak.
int eredmeny = 10 + 2 * 5; // Először 2 * 5 = 10, majd 10 + 10 = 20
printf("%d\n", eredmeny); // 20
int x = 1, y = 2, z = 3;
x = y = z; // Jobbról balra: y = z (y=3), majd x = y (x=3)
printf("x: %d, y: %d, z: %d\n", x, y, z); // x: 3, y: 3, z: 3
A precedencia és asszociativitás táblázatok megértése fontos a komplex kifejezések helyes értelmezéséhez, de a legjobb gyakorlat a zárójelek bőséges használata a kód olvashatóságának javítása érdekében.
Vezérlési szerkezetek: A programfolyamat irányítása
A programok nem mindig lineárisan futnak; gyakran szükség van arra, hogy bizonyos kódrészleteket csak feltétel esetén hajtsunk végre, vagy többször is ismételjünk. Erre szolgálnak a vezérlési szerkezetek.
Elágazások
Az elágazások lehetővé teszik, hogy a program különböző útvonalakon haladjon a feltételek teljesülése alapján.
`if`, `else if`, `else`
A leggyakoribb feltételes utasítás. Lehetővé teszi, hogy egy kódrészletet csak akkor hajtsunk végre, ha egy adott feltétel igaz. Az else if
és else
részek opcionálisak.
int pontszam = 75;
if (pontszam >= 90) {
printf("Jeles\n");
} else if (pontszam >= 80) {
printf("Jó\n");
} else if (pontszam >= 70) {
printf("Közepes\n");
} else if (pontszam >= 60) {
printf("Elégséges\n");
} else {
printf("Elégtelen\n");
}
Ha egy if
vagy else
blokk csak egyetlen utasítást tartalmaz, a kapcsos zárójelek elhagyhatók, de a kód olvashatósága és hibák elkerülése érdekében ajánlott mindig használni őket.
`switch` utasítás
A switch
utasítás egy alternatíva az if-else if
láncra, amikor egy változó különböző lehetséges egész értékét vizsgáljuk. Tisztábbá és olvashatóbbá teheti a kódot, ha sok feltétel van ugyanarra a változóra.
char muvelet = '+';
int a = 10, b = 5;
int eredmeny;
switch (muvelet) {
case '+':
eredmeny = a + b;
printf("Összeg: %d\n", eredmeny);
break;
case '-':
eredmeny = a - b;
printf("Különbség: %d\n", eredmeny);
break;
case '*':
eredmeny = a * b;
printf("Szorzat: %d\n", eredmeny);
break;
case '/':
if (b != 0) {
eredmeny = a / b;
printf("Hányados: %d\n", eredmeny);
} else {
printf("Nullával való osztás!\n");
}
break;
default:
printf("Érvénytelen művelet!\n");
break;
}
- A
switch
utáni zárójelben egy egész típusú kifejezésnek kell lennie (int
,char
,enum
). - A
case
címkéknek konstans egész értékeknek kell lenniük. - A
break;
utasítás rendkívül fontos! Ha elhagyjuk, a program tovább fut a következőcase
blokkba (fall-through), ami gyakran nem kívánt viselkedés. - A
default:
blokk opcionális, és akkor hajtódik végre, ha egyikcase
feltétel sem egyezik.
Ciklusok
A ciklusok lehetővé teszik egy kódrészlet ismételt végrehajtását, amíg egy adott feltétel fennáll.
`for` ciklus
Ideális, ha előre tudjuk, hányszor kell ismételni egy műveletet, vagy ha egy számláló változót használunk az iterációhoz.
for (inicializálás; feltétel; léptetés) {
// Kód, ami ismétlődik
}
Példa: Számok kiírása 1-től 5-ig:
for (int i = 1; i <= 5; i++) {
printf("%d\n", i);
}
inicializálás
: Egyszer hajtódik végre a ciklus elején (pl. számláló deklarálása és kezdőérték adása).feltétel
: Minden iteráció előtt kiértékelődik. Ha igaz, a ciklus folytatódik; ha hamis, a ciklus leáll.léptetés
: Minden iteráció végén hajtódik végre (pl. a számláló növelése vagy csökkentése).
`while` ciklus
Ideális, ha a ciklusmegállás feltétele nem egy számlálóhoz kötött, hanem valamilyen eseményhez vagy állapotváltozáshoz.
while (feltétel) {
// Kód, ami ismétlődik
}
Példa: Addig olvas be számokat, amíg a felhasználó nullát nem ad meg:
int szam;
printf("Adjon meg számokat (0 a kilépéshez):\n");
scanf("%d", &szam); // Első beolvasás
while (szam != 0) {
printf("Beolvasott szám: %d\n", szam);
scanf("%d", &szam); // Következő beolvasás
}
printf("Kilépés a ciklusból.\n");
Fontos, hogy a ciklus törzsében legyen valami, ami a feltételt végül hamissá teszi, különben végtelen ciklusba kerülünk.
`do-while` ciklus
Hasonló a while
ciklushoz, de garantálja, hogy a ciklus törzse legalább egyszer végrehajtódik, mert a feltételt a ciklus végén ellenőrzi.
do {
// Kód, ami ismétlődik
} while (feltétel);
Példa: Menü megjelenítése és választás bekérése, amíg érvényes választ nem kapunk:
int valasztas;
do {
printf("1. Új játék\n");
printf("2. Beállítások\n");
printf("3. Kilépés\n");
printf("Válasszon: ");
scanf("%d", &valasztas);
} while (valasztas < 1 || valasztas > 3);
printf("Választott: %d\n", valasztas);
Ugró utasítások
Ezek az utasítások módosítják a ciklusok vagy függvények normál végrehajtási folyamatát.
`break`
Kilép a legközelebbi enclosing for
, while
, do-while
vagy switch
blokkból. A program végrehajtása a ciklus/switch
utáni első utasítással folytatódik.
for (int i = 1; i <= 10; i++) {
if (i == 5) {
break; // Kilép a ciklusból, ha i = 5
}
printf("%d ", i); // Kimenet: 1 2 3 4
}
printf("\nCiklusból kilépve.\n");
`continue`
Átugorja a ciklus aktuális iterációjának hátralévő részét, és a következő iterációval folytatja (a feltétel ellenőrzésével és a léptetéssel).
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Átugorja a 3-as iterációt
}
printf("%d ", i); // Kimenet: 1 2 4 5
}
printf("\nCiklus vége.\n");
`goto` (és miért kerüljük)
A goto
utasítás egy címkére ugrik a kódban. Bár létezik, használatát általában kerülni kell, mert "spagetti kódhoz" vezethet, ami nehezen olvasható, karbantartható és hibakereshető. Ritka esetekben, például hibakezelésnél vagy beágyazott ciklusokból való kilépésnél használható, de a modern programozásban szinte mindig van jobb, strukturáltabb alternatíva (függvények, break
, continue
).
// Rossz példa, kerülendő!
int i = 0;
loop_start:
printf("%d ", i);
i++;
if (i < 5) {
goto loop_start;
}
printf("\n");
A vezérlési szerkezetek – elágazások és ciklusok – a programozás alapkövei. Megfelelő használatuk elengedhetetlen a logikus és hatékony programok írásához. A
break
éscontinue
hasznosak lehetnek a ciklusok finomhangolására, míg agoto
használatát a legtöbb esetben kerülni kell.
Függvények: A kód modularizálása és újrafelhasználhatósága
A függvények a C programozás alapvető építőkövei. Lehetővé teszik a kód logikai egységekre bontását, a feladatok elkülönítését és a kód újrafelhasználását. Egy függvény egy elnevezett kódrészlet, amely egy specifikus feladatot végez el, és opcionálisan bemeneti paramétereket fogadhat, valamint egy visszatérési értéket adhat vissza.
Függvénydeklaráció és -definíció
Mielőtt egy függvényt meghívnánk, a fordítónak tudnia kell a függvény "aláírását" (signature), azaz a visszatérési típusát, a nevét és a paramétereit. Ezt hívjuk függvénydeklarációnak (vagy prototípusnak). A függvénydefiníció tartalmazza a függvény tényleges kódját.
// Függvénydeklaráció (prototípus)
int osszead(int a, int b);
int main() {
int eredmeny = osszead(5, 3); // Függvényhívás
printf("Az összeadás eredménye: %d\n", eredmeny);
return 0;
}
// Függvénydefiníció
int osszead(int a, int b) {
return a + b;
}
Ha a függvénydefiníció a main()
függvény előtt található, akkor a deklaráció elhagyható, de nagyobb projektekben, ahol a függvények különböző fájlokban helyezkedhetnek el, a deklarációk fejlécfájlokban (.h
kiterjesztésű fájlokban) vannak elhelyezve.
Paraméterátadás (érték szerinti átadás)
Alapértelmezés szerint a C érték szerinti paraméterátadást használ. Ez azt jelenti, hogy amikor egy változót átadunk egy függvénynek, annak egy másolata készül el, és a függvény a másolaton dolgozik. Az eredeti változó értéke nem változik a függvényen kívül.
void novel(int szam) {
szam = szam + 1;
printf("A függvényen belül: %d\n", szam);
}
int main() {
int x = 10;
printf("Függvényhívás előtt: %d\n", x); // Kimenet: 10
novel(x);
printf("Függvényhívás után: %d\n", x); // Kimenet: 10 (nem változott)
return 0;
}
Ha a függvénynek módosítania kellene az eredeti változó értékét, mutatókat kell használnunk, amit később tárgyalunk.
Visszatérési értékek
A függvények egy értéket adhatnak vissza a hívó kódnak a return
kulcsszóval. A visszatérési érték típusa megegyezik a függvény deklarációjában megadott típussal.
double terulet_szamitas(double sugar) {
return 3.14159 * sugar * sugar;
}
int main() {
double r = 5.0;
double t = terulet_szamitas(r);
printf("A kör területe: %f\n", t);
return 0;
}
`void` típus
Ha egy függvény nem ad vissza értéket, a visszatérési típusa void
. Hasonlóan, ha egy függvény nem vesz fel paramétereket, a paraméterlistájában használhatjuk a void
kulcsszót (bár az üres zárójel is elfogadott).
void udvozles() {
printf("Üdvözlöm!\n");
}
int main() {
udvozles(); // Függvényhívás
return 0;
}
Rekurzió
Egy függvény rekurzív, ha saját magát hívja meg. Fontos, hogy legyen egy alapfeltétel (base case), amely leállítja a rekurziót, különben végtelen ciklusba (stack overflow) kerülhetünk.
Példa: Faktoriális számítása rekurzióval
long long faktorialis(int n) {
if (n == 0 || n == 1) {
return 1; // Alapfeltétel
} else {
return n * faktorialis(n - 1); // Rekurzív hívás
}
}
int main() {
printf("5 faktoriális: %lld\n", faktorialis(5)); // Kimenet: 120
return 0;
}
A rekurzió elegáns megoldásokat kínálhat bizonyos problémákra (pl. fa-struktúrák bejárása, gyorsrendezés), de memóriaintenzív lehet, mivel minden rekurzív hívás új veremkeretet (stack frame) hoz létre.
Standard könyvtári függvények
A C nyelv számos előre definiált függvényt biztosít a standard könyvtárban, amelyeket fejlécfájlok (pl. stdio.h
, stdlib.h
, string.h
, math.h
) beillesztésével használhatunk. Ezek jelentősen felgyorsítják a fejlesztést, mivel nem kell "feltalálni a kereket" minden alapvető művelethez.
stdio.h
:printf()
,scanf()
,fopen()
,fclose()
stb. (standard I/O)stdlib.h
:malloc()
,free()
,rand()
,exit()
stb. (általános segédprogramok)string.h
:strcpy()
,strlen()
,strcmp()
stb. (sztringkezelés)math.h
:sqrt()
,sin()
,cos()
,pow()
stb. (matematikai függvények)
Ezeknek a függvényeknek a használatával hatékony és megbízható programokat írhatunk.
Tömbök: Az azonos típusú adatok gyűjteményei
A tömb egy azonos típusú adatelemek rendezett gyűjteménye, amelyek folytonosan helyezkednek el a memóriában. A tömb elemeit indexekkel érhetjük el.
Egydimenziós tömbök deklarálása és inicializálása
Egy tömb deklarálásakor meg kell adni az elemek típusát és a tömb méretét (az elemek számát).
int szamok[5]; // Deklarál egy 5 egész számból álló tömböt
// Inicializálás értékadással
szamok[0] = 10;
szamok[1] = 20;
// ...
szamok[4] = 50;
// Deklaráció és inicializálás egy lépésben
int pontszamok[] = {85, 92, 78, 65, 95}; // A méret automatikusan 5 lesz
int napok[7] = {1, 2, 3, 4, 5, 6, 7};
Ha az inicializáláskor kevesebb elemet adunk meg, mint amennyi a tömb mérete, a fennmaradó elemek automatikusan nullára inicializálódnak (vagy a megfelelő típus alapértelmezett értékére).
int reszleges[5] = {1, 2}; // reszleges: {1, 2, 0, 0, 0}
Tömbök elérése indexekkel
A tömb elemei 0-tól indexelődnek, azaz az első elem indexe 0, a másodiké 1, és így tovább. Egy N
méretű tömb utolsó elemének indexe N-1
.
int tomb[3] = {10, 20, 30};
printf("Első elem: %d\n", tomb[0]); // Kimenet: 10
printf("Harmadik elem: %d\n", tomb[2]); // Kimenet: 30
// Hiba: Tömbhatáron kívüli hozzáférés (undefined behavior)
// tomb[3] = 40; // Ez hibát okozhat, vagy váratlan eredményt adhat
A C nyelv nem végez futásidejű indexhatár-ellenőrzést, ami gyorssá teszi, de a programozó felelőssége, hogy ne lépje túl a tömb határait. A tömbhatáron kívüli hozzáférés (out-of-bounds access) gyakori hibaforrás, amely memóriasérüléshez vagy biztonsági résekhez vezethet.
Tömbök bejárása ciklusokkal
A ciklusok ideálisak a tömbök elemeinek bejárására és feldolgozására.
int szamok[] = {10, 20, 30, 40, 50};
int meret = sizeof(szamok) / sizeof(szamok[0]); // A tömb méretének kiszámítása
printf("A tömb elemei:\n");
for (int i = 0; i < meret; i++) {
printf("%d ", szamok[i]);
}
printf("\n");
A sizeof(tomb) / sizeof(tomb[0])
egy gyakori módja a tömb méretének (elemek számának) megállapítására, feltéve, hogy a tömb nem egy függvény paramétereként lett átadva (akkor a tömb már csak egy mutatóként viselkedik, és a sizeof
a mutató méretét adná vissza, nem a tömbét).
Többdimenziós tömbök (mátrixok)
A C támogatja a többdimenziós tömböket is, amelyek leggyakrabban kétdimenziós mátrixok formájában fordulnak elő.
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printf("Mátrix elemei:\n");
for (int i = 0; i < 2; i++) { // Sorok
for (int j = 0; j < 3; j++) { // Oszlopok
printf("%d ", matrix[i][j]);
}
printf("\n");
}
A matrix[0][0]
az első sor első eleme, matrix[1][2]
a második sor harmadik eleme.
Karaktertömbök és sztringek bevezetése
A C nyelvben a sztringek valójában karaktertömbök, amelyek egy nullterminátorral (\0
) végződnek. Ez a nullterminátor jelzi a sztring végét.
char nev[10] = "Péter"; // A méret 10, mert "Péter" (5 karakter) + \0 (1 karakter) = 6 helyet foglal
printf("Név: %s\n", nev);
char uzenet[] = "Hello"; // A méret automatikusan 6 lesz (5 karakter + \0)
printf("Üzenet: %s\n", uzenet);
A sztringek kezeléséhez a <string.h>
fejlécfájlban található függvényeket használjuk, mint például a strlen()
, strcpy()
, strcat()
, strcmp()
, amiket a Sztringek kezelése C-ben részben tárgyalunk részletesebben.
A tömbök alapvető adatszerkezetek a C-ben, amelyek azonos típusú adatok rendezett gyűjteményeit tárolják. Fontos az indexelés megértése (0-tól kezdődik) és a tömbhatáron kívüli hozzáférés elkerülése a stabil és biztonságos programok érdekében.
Mutatók (Pointers): A C programozás szíve és lelke

A mutatók a C nyelv egyik legfontosabb és egyben legbonyolultabbnak tartott jellemzői. Egy mutató egy változó, amely egy másik változó memóriacímét tárolja. A mutatók segítségével közvetlenül hozzáférhetünk a memóriához, ami rendkívül hatékony és rugalmas programozást tesz lehetővé, de egyúttal felelősséggel is jár, mivel könnyen okozhatunk vele memóriasérülést, ha nem bánunk velük óvatosan.
Mi a mutató? Memóriacímek
Minden változó a számítógép memóriájában tárolódik egy bizonyos címen. Amikor deklarálunk egy változót, a fordító lefoglal neki egy memóriaterületet. Egy mutató változó nem az értéket tárolja, hanem azt a memóriacímet, ahol az érték található.
Képzeljük el a memóriát egy nagy, számozott széfekből álló falnak. Egy normál változó egy széf, ami tartalmaz egy értéket. Egy mutató egy papírdarab, amire rá van írva egy széf száma.
Mutatók deklarálása és inicializálása
Egy mutató deklarálásakor meg kell adni, hogy milyen típusú adatra mutat. Ez fontos, mert a fordító tudni fogja, hány bájtot kell olvasnia/írnia a mutatott címen.
int* pSzam; // Deklarál egy mutatót egész számra
char* pKarakter; // Deklarál egy mutatót karakterre
double* pAr; // Deklarál egy mutatót double típusra
A *
szimbólum jelzi, hogy egy mutatót deklarálunk. Fontos megjegyezni, hogy a *
a típushoz tartozik, nem a változónévhez, bár sokan így írják: int *pSzam;
(mindkét forma elfogadott).
Inicializáláskor egy mutatót egy változó címével kell feltölteni, vagy NULL
-ra kell állítani, ha még nem mutat semmire.
int szam = 10;
int* pSzam = &szam; // pSzam most a 'szam' változó memóriacímét tárolja
char* pNev = NULL; // pNev nem mutat semmire
Cím operátor (`&`) és indirekció operátor (`*`)
- Cím operátor (
&
): Ezt az operátort "address-of" operátornak is nevezik. Egy változó elé helyezve visszaadja annak memóriacímét. - Indirekció operátor (
*
): Ezt az operátort "dereference" operátornak is nevezik. Egy mutató elé helyezve hozzáfér a mutató által mutatott memóriacímen lévő értékhez.
int ertek = 100;
int* pErtek = &ertek; // pErtek tárolja az ertek címét
printf("Az ertek változó értéke: %d\n", ertek); // Kimenet: 100
printf("Az ertek változó címe: %p\n", (void*)pErtek); // Kimenet: pl. 0x7ffe... (memóriacím)
printf("A pErtek által mutatott érték: %d\n", *pErtek); // Kimenet: 100
*pErtek = 200; // Módosítjuk az értéket a mutató segítségével
printf("Az ertek változó új értéke: %d\n", ertek); // Kimenet: 200
A %p
formátumjelölő a mutatók (címek) kiírására szolgál, és gyakran (void*)
-ra kell kasztolni a mutatót a kompatibilitás érdekében.
Mutató aritmetika
A mutatókon aritmetikai műveleteket is végezhetünk, de ezek nem úgy működnek, mint a normál számokon. Amikor egy mutatót növelünk (p++
) vagy csökkentünk (p--
), a mutató nem egy bájttal lép előre, hanem a mutatott típus méretével. Ez rendkívül hasznos tömbök bejárásakor.
int szamok[] = {10, 20, 30};
int* p = szamok; // p most a szamok[0] címére mutat
printf("p értéke: %d\n", *p); // Kimenet: 10
p++; // p most a szamok[1] címére mutat (p 4 bájttal növekedett, ha int 4 bájt)
printf("p új értéke: %d\n", *p); // Kimenet: 20
p = p + 1; // Ugyanaz, mint p++
printf("p még újabb értéke: %d\n", *p); // Kimenet: 30
Csak egész számokat adhatunk hozzá mutatókhoz vagy vonhatunk ki belőlük. Két azonos típusú mutatót kivonhatunk egymásból, ami az elemek számát adja vissza köztük.
Mutatók és tömbök kapcsolata
C-ben a tömb neve egy konstans mutatóként viselkedik az első elem címére. Ezért a tömbök és mutatók gyakran felcserélhetők bizonyos kontextusokban.
int arr[] = {10, 20, 30};
int* ptr = arr; // ptr most az arr[0] címére mutat
printf("%d\n", arr[0]); // Hagyományos tömb hozzáférés
printf("%d\n", *ptr); // Mutatóval való hozzáférés (arr[0])
printf("%d\n", arr[1]); // arr[1]
printf("%d\n", *(ptr + 1)); // *(ptr + 1) egyenlő arr[1]
Ez a szoros kapcsolat teszi a mutatókat alapvetővé a tömbökkel való hatékony munkához, különösen függvények paramétereiként.
Mutatók függvényparaméterként (referencia szerinti átadás)
Ahogy korábban említettük, a C alapértelmezetten érték szerinti paraméterátadást használ. Ha egy függvénynek módosítania kell egy változó értékét a hívó függvény hatókörében, akkor a változó címét kell átadni a függvénynek (mutatóként), és a függvényen belül a mutatót dereferálva módosítani az értéket. Ezt nevezzük referencia szerinti átadásnak.
void csere(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
printf("Csere előtt: x = %d, y = %d\n", x, y); // x = 5, y = 10
csere(&x, &y); // Átadjuk x és y címét
printf("Csere után: x = %d, y = %d\n", x, y); // x = 10, y = 5
return 0;
}
Itt a csere
függvény nem az x
és y
értékét kapja meg, hanem a memóriacímeiket. A *a
és *b
segítségével hozzáfér a memóriában lévő eredeti értékekhez, és módosítja azokat.
`NULL` mutatók
Egy NULL
mutató nem mutat semmilyen érvényes memóriacímre. Ajánlott minden mutatót NULL
-ra inicializálni, ha nem tudjuk azonnal érvényes címmel feltölteni. Ez segít elkerülni a "wild pointer" hibákat, és lehetővé teszi a mutató érvényességének ellenőrzését.
int* p = NULL; // Jó gyakorlat
// ...
if (p != NULL) {
// Csak akkor dereferáljuk, ha nem NULL
printf("Érték: %d\n", *p);
} else {
printf("A mutató NULL.\n");
}
Egy NULL
mutató dereferálása futásidejű hibához (segmentation fault) vezet.
Dangling pointers, wild pointers
- Dangling pointer (függő mutató): Akkor keletkezik, ha egy mutató egy olyan memóriaterületre mutat, amelyet már felszabadítottak vagy ami már nem érvényes (pl. egy lokális változóra, ami már kiment a hatókörből). A felszabadított memória tartalmát más programrészek felülírhatják, így a függő mutató használata kiszámíthatatlan eredményekhez vezethet.
- Wild pointer (vad mutató): Egy nem inicializált mutató, amely véletlenszerű memóriacímre mutat. Ha egy ilyen mutatót dereferálunk, az szintén kiszámíthatatlan viselkedést okozhat, memóriasérüléshez vagy program összeomláshoz vezethet.
Mindkettő súlyos hiba, amelyet el kell kerülni a gondos programozással és a mutatók helyes kezelésével.
Mutatók mutatókra
Egy mutató mutathat egy másik mutatóra. Ezt dupla mutatóként (pointer-to-pointer) ismerjük, és **
szimbólummal deklaráljuk.
int szam = 10;
int* p1 = &szam; // p1 a szam címére mutat
int** p2 = &p1; // p2 a p1 címére mutat
printf("Érték a szam-on keresztül: %d\n", szam); // 10
printf("Érték a p1-en keresztül: %d\n", *p1); // 10
printf("Érték a p2-n keresztül: %d\n", **p2); // 10
printf("p1 címe: %p\n", (void*)p1);
printf("szam címe a p2-n keresztül: %p\n", (void*)*p2); // Ugyanaz, mint p1 címe
A mutatók mutatókra hasznosak lehetnek például dinamikus tömbök tömbjeinek (mátrixok) kezelésére vagy olyan függvények írására, amelyek mutatók értékét módosítják (pl. memóriafoglaló függvények, amelyek egy mutatót töltenek fel a lefoglalt memória címével).
A mutatók a C nyelv kulcsfontosságú elemei, amelyek közvetlen memóriahozzáférést biztosítanak. Bár bonyolultnak tűnhetnek, alapvető fontosságúak a hatékony memóriakezeléshez, a referencia szerinti paraméterátadáshoz és a dinamikus adatszerkezetek implementálásához. Megértésük és helyes használatuk elengedhetetlen a profi C programozáshoz.
Dinamikus memóriakezelés: Rugalmas memóriafoglalás futásidőben
A C nyelvben a változók memóriája alapvetően két helyen foglalódik le: a veremben (stack) és a kupacban (heap). A veremben tárolt változók (lokális változók, függvényparaméterek) mérete fordítási időben ismert, és automatikusan felszabadulnak, amikor a függvény befejeződik. A kupacban (dinamikus memória) azonban futásidőben foglalhatunk le memóriát, ami rugalmasságot biztosít olyan esetekben, amikor nem tudjuk előre a szükséges memória méretét, vagy ha az adatoknak a függvényhívások között is fenn kell maradniuk.
Miért van szükség dinamikus memóriára? (Stack vs. Heap)
- Verem (Stack):
- Fix méretű, a fordítási időben ismert.
- Gyors hozzáférés.
- Automatikus memóriakezelés (LIFO - Last In, First Out).
- Lokális változók és függvényhívások tárolására.
- Korlátozott méret (stack overflow lehetséges nagy rekurzió vagy nagy lokális tömbök esetén).
- Kupac (Heap):
- Rugalmas méretű, futásidőben foglalható.
- Lassabb hozzáférés.
- Manuális memóriakezelés (a programozó felelős a foglalásért és fels