Vezérlőpult
E3. Túlcímzés · Tömbök, listák, Dictionary

E blokk · Tömbök, listák, Dictionary · 3. tananyag

Túlcímzés: az IndexOutOfRangeException

A tömbös programok leggyakoribb hibája, hogy az index kilóg a 0 … Length − 1 tartományból. A fordító ezt nem veszi észre, mert az index többnyire csak futás közben derül ki — a program ilyenkor kivétellel leáll. Ma megtanulod elolvasni a hibaüzenetet, felismerni a tipikus okokat, és úgy írni a kódot, hogy a hiba elő se forduljon. És a C1-es rövidzár itt végre hasznos lesz.

1 tanórakivétel · hibaüzenetmegelőzés · rövidzár

Tanulási célok

1Amikor az index kilóg

int[] jegyek = { 5, 4, 3 };
Console.WriteLine(jegyek[3]);

A kód lefordul, de futtatáskor a program megáll, és ezt írja ki:

Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Program.<Main>$(String[] args) in C:\...\Program.cs:line 2

Olvasd el részenként:

RészJelentés
Unhandled exceptionkezeletlen kivétel — senki nem „kapta el”, ezért a program leállt
System.IndexOutOfRangeExceptiona kivétel típusa: az index a tartományon kívül esett
Index was outside the bounds of the array.a leírás: az index a tömb határain kívül volt
line 2a sor, ahol a hiba történt — innen indul a nyomozás

Visual Studióban futtatva a program a hibás soron áll meg, és egy ablak (Exception Helper) mutatja ugyanezt. A hibás sor ki van emelve: ezen a ponton a változók értéke még megvizsgálható (5. szakasz).

2Mi a kivétel?

A kivétel (exception) egy futás közben keletkező hiba, amely objektumként jelentkezik: van típusa (IndexOutOfRangeException), üzenete és helye. Ha a program nem kezeli le, leáll.

Már találkoztál ilyennel: a B6-ban az int.Parse("alma") egy FormatException-t okozott. A mechanizmus ugyanaz, csak a hiba más. A kivételek kezelését (try–catch) a K blokkban tanuljuk; addig a cél az, hogy ne keletkezzenek — és erre a tömböknél jó eszközeink vannak.

ℹ️ MIÉRT NEM JELEZ A FORDÍTÓ?

Mert az index legtöbbször változó: jegyek[i], jegyek[sorszam - 1]. Hogy az i éppen mennyi, az csak futás közben derül ki. (Ha konstans indexet írsz, mint a jegyek[3], a fordító akkor sem ellenőrzi, hogy a tömb mekkora.)

3A tipikus okok

<= a ciklusfeltételben — a legismertebb eset, a C4-es off-by-one tömbös változata:

for (int i = 0; i <= jegyek.Length; i++)   // HIBÁS: az utolsó kör i = Length
{
    Console.WriteLine(jegyek[i]);
}

1-től számolt sorszám — a felhasználó 1–7-ig ad meg napot, a tömb 0–6-ig indexel:

int nap = Konzol.SzamotKer("Nap (1–7): ", 1, 7);
Console.WriteLine(napok[nap]);             // HIBÁS: a 7-esre túlcímez
Console.WriteLine(napok[nap - 1]);         // helyes

A szomszédos elem — ha minden elemet a következővel hasonlítasz, az utolsónak nincs következője:

Hibás
for (int i = 0; i < t.Length; i++)
{
    if (t[i] > t[i + 1]) { … }
}
// az utolsó körben t[i + 1] túlmutat
Helyes
for (int i = 0; i < t.Length - 1; i++)
{
    if (t[i] > t[i + 1]) { … }
}
// az utolsó előtti elemig megy

Üres tömb — egy nulla elemű tömbnek első eleme sincs: a t[0] is kivételt okoz. Ha a méret bekért érték, az üres eset is előfordulhat.

Negatív index — például egy t[i - 1] szomszéd a 0. elemnél.

4Megelőzés

if (index >= 0 && index < t.Length)
{
    Console.WriteLine(t[index]);
}
else
{
    Konzol.Hiba("Nincs ilyen sorszámú elem.");
}

A rövidzár — a C1-es ígéret: a && jobb oldala csak akkor értékelődik ki, ha a bal oldala igaz. Ezért az ellenőrzés sorrendje számít:

if (i < t.Length && t[i] > 0)    // helyes: ha i túl nagy, a t[i]-t meg sem nézi
if (t[i] > 0 && i < t.Length)    // HIBÁS: előbb indexel, csak utána ellenőriz
💡 ÍGÉRET TELJESÍTVE

A C1-ben a rövidzárat „csak érdekességként” említettük, azzal, hogy az E blokkban hasznot húzunk belőle. Itt az idő: az első változat soha nem címez túl, mert ha az i < t.Length hamis, a C# a jobb oldalt ki sem értékeli.

Lefut vagy kivétel?

Ahol nincs külön megadva: int[] t = { 3, 6, 9 };

1 / 8Pontszám: 0
…

5Nyomozás a debuggerrel

Ha a kivétel mégis bekövetkezik:

  1. Futtasd a programot Visual Studióból (F5) — a hibás soron megáll.
  2. Nézd meg a Locals ablakban (A9) az index változó (i) és a tömb értékét — a tömb kinyitható, a mérete is látszik.
  3. Hasonlítsd össze: az index hány, a legnagyobb érvényes index hány?
  4. Ha nem világos, hogyan lett ilyen az index: tegyél töréspontot a ciklus elejére, és lépkedj F10-zel, figyelve az i-t.

6Gyakori hibák

TünetMi történt?Mit tegyél?
a ciklus utolsó körében kivételi <= t.Lengthi < t.Length
a legnagyobb sorszámnál kivétel1-től számolt sorszám, 0-tól számolt tömbt[sorszam - 1]
szomszéd-összehasonlításnál kivétel az utolsó elemnéla ciklus Length-ig megy, a t[i + 1] túlmutata ciklus t.Length - 1-ig menjen
üres tömbnél kivétel a t[0]-na tömbnek nincs elemeelőbb ellenőrizd: t.Length > 0
az ellenőrzés ellenére kivételrossz sorrend: t[i] > 0 && i < t.Lengthelőbb a tartomány, aztán az elem (rövidzár)
ArgumentOutOfRangeExceptionugyanez a hiba List-nél (E4)a List méretét a Count adja

7Gyakorlat — hibavadászat

Az alábbi program három helyen címez túl. Másold be egy új Hibavadaszat projektbe (a Konzol.cs-szel együtt), futtasd, és javítsd ki egyenként a hibákat — mindegyiknél jegyezd fel kommentben, mi volt az ok.

int[] homerseklet = { 12, 15, 11, 18, 20, 17, 14 };
string[] napok = { "hétfő", "kedd", "szerda", "csütörtök", "péntek", "szombat", "vasárnap" };

for (int i = 0; i <= homerseklet.Length; i++)
{
    Console.WriteLine($"{napok[i]}: {homerseklet[i]} fok");
}

int nap = Konzol.SzamotKer("Melyik nap (1–7)? ", 1, 7);
Console.WriteLine($"{napok[nap]}: {homerseklet[nap]} fok");

for (int i = 0; i < homerseklet.Length; i++)
{
    if (homerseklet[i + 1] > homerseklet[i])
    {
        Console.WriteLine($"{napok[i + 1]}: melegedés");
    }
}

Commit: Hibavadaszat: harom tulcimzes javitva.

✅ ELLENŐRZŐ PONT

A program végigfut; a napok és hőmérsékletek listája teljes; mind a hét sorszámra a helyes nap jelenik meg; a melegedést jelző rész a keddtől a vasárnapig vizsgál.

8Feladatok

Gyakorló feladatok

⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás

E3.1⭐

Írj szándékosan túlcímző programot, futtasd, és másold ki a hibaüzenetet. Jelöld meg benne a kivétel típusát és a hibás sor számát.

E3.2⭐

Egy 5 elemű tömbből a felhasználó adja meg, hányadik elemet akarja látni, és a program írja ki — tartomány-ellenőrzéssel, hibás sorszámra piros üzenettel.

E3.3⭐⭐

Írj bool BiztonsagosElem(int[] t, int index, out int ertek) metódust (a D3-as TryParse-minta!), amely érvényes indexnél igazat és az elemet adja, érvénytelennél hamisat.

E3.4⭐⭐

Egy tömbben napi árfolyamok vannak. Írd ki, hány napon nőtt az árfolyam az előző naphoz képest — túlcímzés nélkül.

E3.5⭐⭐⭐

Kérj be egy számot (0 is lehet), hozz létre ekkora tömböt, töltsd fel, és írd ki az első és az utolsó elemét — a program üres tömbnél se álljon le, hanem írja ki, hogy nincs elem.

9Önellenőrző kvíz

Ellenőrizd magad!

Minden kérdésnél egy válasz helyes. A végén pontszámot kapsz.

10Fogalomtár

FogalomJelentés
túlcímzésa tömb érvényes indextartományán (0 … Length − 1) kívüli index használata
kivétel (exception)futás közben keletkező hiba, típussal és üzenettel; kezeletlenül leállítja a programot
IndexOutOfRangeExceptiona túlcímzéskor keletkező kivétel típusa
kezeletlen kivételolyan kivétel, amelyet a program nem kap el — ekkor leáll (kezelés: K blokk)
tartomány-ellenőrzésaz index vizsgálata használat előtt: index >= 0 && index < t.Length
rövidzára && jobb oldala csak igaz bal oldalnál értékelődik ki — ezért számít a feltételek sorrendje

11Összegzés

  1. A túlcímzés futás közbeni hiba: a program IndexOutOfRangeException kivétellel leáll — a fordító nem jelzi, mert az index futás közben dől el.
  2. A hibaüzenetből a kivétel típusa, leírása és a sor száma olvasható ki.
  3. Tipikus okok: <= a feltételben, 1-től számolt sorszám, szomszédos elem az utolsónál, üres tömb.
  4. Megelőzés: i < t.Length, foreach, tartomány-ellenőrzés — és a rövidzár miatt előbb a tartomány, aztán az elem.
  5. A kivételek kezelését (try–catch) a K blokk tanítja; addig a cél, hogy ne keletkezzenek.
  6. Következik: a tömb rögzített méretének korlátja, és a megoldás — a List<T>.