E blokk · Tömbök, listák, Dictionary · 3. tananyag
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.
IndexOutOfRangeException hibaüzenetét.<= a feltételben, 1-től számolt
sorszám, szomszédos elem, üres tömb).foreach, tartomány-ellenőrzés.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ész | Jelentés |
|---|---|
Unhandled exception | kezeletlen kivétel — senki nem „kapta el”, ezért a program leállt |
System.IndexOutOfRangeException | a 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 2 | a 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).
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.
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.)
<= 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:
for (int i = 0; i < t.Length; i++) { if (t[i] > t[i + 1]) { … } } // az utolsó körben t[i + 1] túlmutat
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.
i < t.Length
— mindig < és
Length, soha nem beírt szám.foreach, ahol lehet:
index nélkül nincs mit elcímezni
(E2).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
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.
Ahol nincs külön megadva: int[] t = { 3, 6, 9 };
Ha a kivétel mégis bekövetkezik:
i) és a tömb értékét — a
tömb kinyitható, a mérete is látszik.i-t.| Tünet | Mi történt? | Mit tegyél? |
|---|---|---|
| a ciklus utolsó körében kivétel | i <= t.Length | i < t.Length |
| a legnagyobb sorszámnál kivétel | 1-től számolt sorszám, 0-tól számolt tömb | t[sorszam - 1] |
| szomszéd-összehasonlításnál kivétel az utolsó elemnél | a ciklus Length-ig megy, a t[i + 1] túlmutat | a ciklus t.Length - 1-ig menjen |
üres tömbnél kivétel a t[0]-n | a tömbnek nincs eleme | előbb ellenőrizd: t.Length > 0 |
| az ellenőrzés ellenére kivétel | rossz sorrend: t[i] > 0 && i < t.Length | előbb a tartomány, aztán az elem (rövidzár) |
ArgumentOutOfRangeException | ugyanez a hiba List-nél (E4) | a List méretét a Count adja |
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.
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.
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás
Í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.
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.
Í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.
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.
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.
Minden kérdésnél egy válasz helyes. A végén pontszámot kapsz.
| Fogalom | Jelentés |
|---|---|
| túlcímzés | a 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 |
IndexOutOfRangeException | a túlcímzéskor keletkező kivétel típusa |
| kezeletlen kivétel | olyan kivétel, amelyet a program nem kap el — ekkor leáll (kezelés: K blokk) |
| tartomány-ellenőrzés | az index vizsgálata használat előtt: index >= 0 && index < t.Length |
| rövidzár | a && jobb oldala csak igaz bal oldalnál értékelődik ki — ezért számít a feltételek sorrendje |
IndexOutOfRangeException kivétellel leáll
— a fordító nem jelzi, mert az index futás közben dől el.<= a feltételben,
1-től számolt sorszám, szomszédos elem az utolsónál, üres tömb.i < t.Length,
foreach, tartomány-ellenőrzés — és a
rövidzár miatt előbb a tartomány, aztán az
elem.try–catch) a
K blokk tanítja; addig a cél, hogy ne keletkezzenek.List<T>.