off-by-one error
Főnév
off-by-one error (tsz. off-by-one errors)
- (informatika) Az off-by-one error az egyik leggyakoribb programozási hiba, amely akkor fordul elő, amikor egy ciklus, tömb- vagy lista-kezelés, számlálás vagy bármilyen iteratív feldolgozás során egy elemmel kevesebbszer vagy többször fut le a kód, mint ahogyan azt a programozó tervezte.
Röviden: pontosan eggyel kevesebb vagy több lépést hajtunk végre, mint kéne.
🚦 Hol fordul elő leggyakrabban?
👉 Ciklusokban:
- for
- while
- do-while
👉 Tömbök, listák bejárásakor (indexelés):
std::vectorstd::array- nyers C-tömbök (
int arr[])
👉 Számlálásokban:
- pl. amikor 10 elemet akarunk kiírni, de csak 9-et írunk ki (vagy 11-et)
👉 String kezelésében:
- karakterenkénti feldolgozás
- ‘\0’ terminátor figyelmen kívül hagyása vagy túllépése
🤔 Miért történik meg?
1️⃣ Programozási hibák az indexelésben
Sok nyelvben, így C++-ban is, a tömbök 0-tól indexelnek:
- Az első elem indexe: 0
- Az utolsó elem indexe:
size - 1
Nagyon gyakran félreértjük:
for (int i = 1; i <= 10; i++) // 1-től 10-ig — itt OK ha 1-től akarunk számozni
De:
for (int i = 0; i <= 10; i++) // Hoppá! Ha 0-tól 10-ig megyek egy 10 elemű tömbön, túlindexelem.
Ha egy 10 elemű tömböt járok be:
- Helyes:
i < 10(index 0..9) - Hibás:
i <= 10→ off-by-one error
2️⃣ Feltételhibák a ciklusban
Nagyon gyakran a feltételben rossz operátort választunk:
for (int i = 0; i <= N; i++) // gyakori hiba
Ha N egy tömb mérete, a helyes:
for (int i = 0; i < N; i++)
3️⃣ Ciklus végfeltétele rossz megfogalmazása
Sok kezdő programozó bizonytalan abban, hogy meddig kell futni:
- Kezdő index: 0 vagy 1?
- Befejezés:
<,<=,>vagy>=?
📚 Klasszikus példák
1️⃣ String bejárása hibásan
char str[] = "Hello";
for (int i = 0; i <= strlen(str); i++) { // HIBA
std::cout << str[i];
}
Helyes:
for (int i = 0; i < strlen(str); i++) {
std::cout << str[i];
}
Miért? strlen(str) nem tartalmazza a \0 terminátort. Ha <=-t használunk, akkor véletlenül kiírjuk a \0-t is, ami hibát okozhat.
2️⃣ Tömb bejárása hibásan
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) { // HIBA: i == 5 túlindexelés!
std::cout << arr[i] << " ";
}
Helyes:
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
3️⃣ Ciklus vége hibás
Ha 1-től N-ig akarok számolni:
for (int i = 1; i < N; i++) // Csak 1..N-1-ig fut → HIBA
Helyes:
for (int i = 1; i <= N; i++) // 1..N → helyes
🔍 Miért veszélyes?
✅ A program lefut — nem fordítási hiba. ✅ A hibát sokszor nehéz észrevenni → csak rossz eredményt kapunk. ✅ Néha memóriaterületet túllépünk → undefined behavior, akár crash.
Klasszikus eset:
int arr[5];
arr[5] = 42; // nincs 5. index — memóriarombolás!
💡 Tipikus hibaforrások
| Helyzet | Mi a tipikus off-by-one ok? |
|---|---|
| Tömb bejárása | < vagy <= összekeverése |
| String feldolgozása | strlen / size / terminátor figyelmen kívül hagyása |
| Számlálás | Rosszul definiált kezdő- vagy végérték |
| Range ciklus | i <= N vs. i < N hibás használata |
| Nested loop | Belső és külső ciklus indexe elcsúszik |
🛠️ Hogyan kerüld el?
1️⃣ Szokj rá a for (int i = 0; i < N; i++) mintára
- Ha tömböt, listát jársz be → mindig
i < N - Ne használj
<=ha indexelsz!
2️⃣ Ha tudod, használd range-based for loop-ot:
for (auto val : vec) {
std::cout << val << " ";
}
→ Nincs index, nincs off-by-one hiba.
3️⃣ Használj konstansot:
const int SIZE = 10;
for (int i = 0; i < SIZE; i++) { ... }
→ így biztosan nem tévesztesz el méretet.
4️⃣ Teszteld a következő eseteket:
- Üres tömb (
size == 0) - 1 elemű tömb (
size == 1) - N elemű tömb
Ha ezekben is működik → helyes ciklusod van.
5️⃣ Ellenőrző kérdés:
Hány iterációt vársz? → Ha nem tudod pontosan megmondani, gondold át újra a ciklust!
⚠️ Specialitások, amiknél különösen gyakori
- Multidimenziós tömbök
- Substring feldolgozás
- Fájl sorainak számlálása
- Rendezés, keresés (pl. binary search)
- Numerikus algoritmusok (pl. sum, product)
👨💻 Példa egy elcsúszott algoritmusra
Hibás factorial:
int factorial(int n) {
int result = 1;
for (int i = 1; i < n; i++) { // Hibás! Nem szorzom be az n-t!
result *= i;
}
return result;
}
Helyes:
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) { // Helyes: 1..n
result *= i;
}
return result;
}
🎁 Összefoglalás
✅ Off-by-one error = eggyel kevesebb vagy több iteráció ✅ Gyakori oka: hibás feltétel, rossz határ (<= vs <) ✅ Nehéz észrevenni, mert nem fordítási hiba ✅ Sok adatstruktúra 0-tól indexel, ezért különösen figyelni kell ✅ Legegyszerűbb elkerülni:
- range-based for
- helyes sablon (
for (int i = 0; i < N; i++)) - tudatos határok definiálása
- tesztelés 0, 1, 2 elemre
Végső tanács
Az OBOE az egyik legelső és leggyakoribb “apró, de veszélyes” programozási hiba, amit szinte minden kezdő és haladó programozó elkövet. Ha mindig tisztában vagy azzal, hogy mit akarsz bejárni (indexeket vagy értékeket), akkor ezt a hibát könnyen elkerülheted.
- off-by-one error - Szótár.net (en-hu)
- off-by-one error - Sztaki (en-hu)
- off-by-one error - Merriam–Webster
- off-by-one error - Cambridge
- off-by-one error - WordNet
- off-by-one error - Яндекс (en-ru)
- off-by-one error - Google (en-hu)
- off-by-one error - Wikidata
- off-by-one error - Wikipédia (angol)