Oldal kiválasztása
c++

Tömbök: Az Adatok Kamrája 📦

Képzeld el, hogy sok-sok, ugyanolyan típusú tárgyat kell eltárolnod, például egy raktárban. Ahelyett, hogy minden egyes tárgynak külön dobozt adnál egyedi névvel, sokkal egyszerűbb, ha van egy nagy polcrendszered, aminek van egy neve, és a polcokon belül sorszámokkal hivatkozol az egyes dobozokra. Pontosan ez a lényege a tömbnek (array)! 🤩

A tömb a legegyszerűbb olyan adatszerkezet, ami sok azonos típusú érték (adat) egyidejű tárolására szolgál. A benne lévő értékekre egyetlen névvel (a tömb nevével) hivatkozunk, így nem kell minden egyes elemnek külön változót deklarálni. Ez hatalmas könnyebbség!

Mire jók a tömbök? 🤔

A tömbök gyakran jelentenek logikai csoportosítást is. Például, ha 15 dolgozó pontszámát és 20 vizsgaeredményt szeretnél tárolni, nem egy 35 elemű tömbbe tennéd őket. Sokkal logikusabb egy 15 elemű tömb a pontszámoknak, és egy 20 elemű a vizsgaeredményeknek. A rendezettség a kulcs! 🔑

Fontos! A C++-ban egy tömbben csak azonos típusú elemek szerepelhetnek! (Eltérően például a JavaScripttől, ahol ez másképp működik.) Ha különböző típusú értékeket szeretnél egy helyen tárolni, akkor a struct vagy class adatszerkezeteket kell használnod, de ezekről majd egy későbbi fejezetben lesz szó. 😉

Egy kis félreértés elkerülése végett: Más tananyagokban néha „vektornak” is nevezik a tömböket. Ebben a tananyagban nem tesszük ezt, hogy elkerüljük a keveredést a C++ Standard Template Library (STL) std::vector nevű adatszerkezetével. Az std::vector egy szuper hasznos eszköz, amit az #include <vector> utasítással érhetünk el, és lényegében egy kényelmi funkciókkal ellátott dinamikus tömb – de erről majd később, mert azt imádni fogod! ❤️

Statikus és Dinamikus Tömbök: A Méretezés Kérdése 📏

A C++ kétféle hagyományos tömb használatát teszi lehetővé, a méretezésük alapján:

1. Statikus Tömb: A Fix Méretű Polc 🧱

A statikus tömböt akkor használjuk, ha már a program írásakor, a forráskódban pontosan tudjuk a tömb méretét. A méretét egy egész szám konstans (pl. const int, const unsigned int) segítségével kell megadnunk:

const int ELEMSZAM = 5;      
// Definiáljuk a tömb méretét konstansként
float homersekletek[ELEMSZAM];
// 5 darab float típusú érték tárolására alkalmas tömb

Vagy ha még kényelmesebbek akarunk lenni, és rögtön kezdőértékeket is adunk, a fordító automatikusan kikövetkezteti a tömb méretét a megadott elemek számából! Ez szuper praktikus! 👇

int szamok[] = {5, -2, 3, 128}; 
/* A fordító tudja,
hogy ez 4 elemű tömb, mert 4 értéket adtunk meg! */

Egy fontos megjegyzés a haladóknak (és a jövő programozóinak!): Ha egy statikus tömb méretét NEM egy egész szám konstans segítségével adod meg, hanem például a felhasználótól kérded be futásidőben, a kód nem lesz szabványos! 😱 Ez azt jelenti, hogy:

  1. Nem biztos, hogy minden C++ fordító támogatja ezt a „trukköt”.
  2. Ha a fordítót –pedantic kapcsolóval futtatod, akkor a nem szabványos részt hibaüzenetként (warningként) jelzi majd.
/* Ez NEM szabványos C++ kód statikus tömb esetén: */
int elemszam;
std::cout << "Kerem adja meg az elemszamot: " << std::endl;
std::cin >> elemszam;
double pontszamok[elemszam]; // Hiba! A méretnek konstansnak kellene lennie.

2. Dinamikus Tömb: A Rugalmasan Növelhető Kamra 🤸‍♀️

A dinamikus tömb a szabványos megoldás, ha a tömb méretét:

  • a felhasználótól kérjük be a program futása során.
  • fájlból olvassuk be.
  • később meg szeretnénk változtatni a program futása közben.

Figyelem, kezdők! Bár a dinamikus tömbök nagyon hasznosak, kezdetben inkább az std::vector használatát javaslom! Sokkal biztonságosabb és kényelmesebb, mert automatikusan kezeli a memória felszabadítását. De fontos, hogy ismerkedj meg a dinamikus tömbökkel is, mert aki komolyabban foglalkozik C++-szal, az biztosan találkozni fog velük!

A dinamikus tömbök nagyobb odafigyelést igényelnek, mert a programozó feladata a tömb számára lefoglalt memóriaterület felszabadítása. Ha elfelejted, az „memóriaszivárgáshoz” (memory leak) vezethet! 💧 Emellett kivételkezelést is igényel (ha például nincs elég szabad memória a rendszeren), de erről majd egy későbbi fejezetben lesz szó.

Így definiálunk egy dinamikus tömböt a new operátorral:

int elemszam;
std::cout << "Kerem adja meg az elemszamot: " << std::endl;
std::cin >> elemszam;
// Létrehozunk egy dinamikus tömböt 'elemszam'
darab double típusú elem tárolására
double * adatok = new double[elemszam];
// A '*' jelzi, hogy ez egy pointer (mutató)

Ha kezdőértékeket is adnál dinamikus tömbnek: Dinamikus tömböknél is megadhatsz kezdőértékeket, de az elemszámot akkor is meg kell adni, és annak legalább akkorának kell lennie, mint a kezdőértékek száma:

// 3 darab int típusú elem tárolására alkalmas tömb kezdőértékekkel
int * init_szamok = new int[3] {128, 0, -20};

Memória Felszabadítása: A delete[] Operátor 🗑️

Ha már nincs szükséged egy dinamikus tömb tartalmára és a lefoglalt memóriára, fel kell szabadítanod a delete[] operátorral! Ha ezt elfelejted, a memória „elfoglalt” marad a program futásának végéig, és más programok nem használhatják.

delete[] adatok; // Felszabadítjuk az 'adatok' tömb által használt memóriát

 Miután felszabadítottad a memóriát, ugyanazzal a névvel létrehozhatsz egy eltérő méretű tömböt is, ha újra lefoglalsz neki memóriát:

adatok = new double[elemszam + 1]; // Most egyel nagyobb tömböt hoztunk létre
// ... vagy akár
adatok = new double[elemszam * 2]; // Kétszer akkora tömböt

 Fontos trükk! Ha egy dinamikus tömb méretét szeretnéd megváltoztatni, de meg is akarod tartani a benne lévő adatokat, akkor a következő (bonyolultabb) lépéseket kell megtenned:

  1. Hozzá létre egy új, nagyobb dinamikus tömböt.
  2. Másold át a régi tömb összes elemét az új tömbbe.
  3. Szabadítsd fel a régi tömb memóriáját a delete[] operátorral.
  4. Ezután használhatod az új, nagyobb tömböt.

Ezért is olyan kényelmes az std::vector, mert mindezt automatikusan megteszi helyetted! 😉

Hivatkozás a Tömb Elemeire: Az Indexelés Titka 🤫

A tömb elemeire a tömb nevével és egy indexel (sorszámmal) hivatkozunk, amit szögletes zárójelek ([]) közé teszünk.

A C++-ban az indexelés 0-tól kezdődik! Ez nagyon fontos! 🤯

  • A tömb legelső eleme a 0 indexű elem.
  • A tömb második eleme az 1 indexű elem.
  • Az utolsó elem indexe pedig az elemszám – 1.

Példák:

int t[5]; // Egy 5 elemű int tömb, indexei 0-tól 4-ig
// Az első (0 indexű) elem értékének módosítása
t[0] = 53;
// Az első (0 indexű) elem kiíratása
std::cout << t[0] << std::endl; // Kiírja: 53
// Egy 20 elemű tömb (pl. 'adatok' néven) utolsó elemének bekérése:
// Az utolsó elem indexe 19 (mivel 0-tól indul)
double adatok[20];
std::cout << "Kerem adja meg az utolso elem erteket: " << std::endl;
std::cin >> adatok[19];

Vigyázat! Tömb túlindexelés! 💥 Ha megpróbálsz olyan indexet használni, ami kívül esik a tömb határain (pl. egy 5 elemű tömb esetén t[5]-öt írsz t[4] helyett), az úgynevezett tömb túlindexelés (out-of-bounds access) történik. Ez nagyon súlyos hiba lehet, ami:

  • Jó eséllyel nem fog lefordulni a program, ha a fordító észreveszi.
  • Ha mégis lefordul, futásidejű hibához (runtime error), program összeomláshoz vagy kiszámíthatatlan viselkedéshez vezethet, mivel olyan memóriaterületre próbálsz írni/olvasni, ami nem a te tömbödhöz tartozik! 🤯

Ezzel a tudással már bátran tárolhatsz és kezelhetsz nagyobb adathalmazokat a programjaidban! A tömbök alapvető építőkövei a komplexebb programoknak.

Sziasztok, programozó palánták! 🌳 Készen álltok egy újabb nagy lépésre a C++ kalandban? Eddig megtanultuk, hogyan hozzunk döntéseket a programjainkban. De mi van, ha sokszor kell ugyanazt a dolgot megismételni? Például, ha egy hatalmas tömb összes elemét fel kell dolgozni, vagy egy menüt újra meg újra meg kell jeleníteni? Erre valók a ciklusok! 🔄