Bug “off-by-one” ao iterar em loops
Introdução
O bug off-by-one acontece quando um loop itera uma posição a mais ou a menos do que deveria, geralmente por causa de condições incorretas de início, fim ou ajuste do índice.
No exemplo a seguir, ele aparece ao combinar incremento controlado e manual no índice.
Código com bug
for (i = 0; i < s.length() - 1; i++) {
if (currentValue < nextValue) {
result += nextValue - currentValue;
i++; // incremento manual
} else {
result += currentValue;
}
}
if (i == s.length() - 1) {
result += mapping.get(s.charAt(s.length() - 1));
}
Motivo do erro
O for
já faz i++
ao final de cada iteração. Ao adicionar i++
dentro do if
, o índice avança duas posições quando há subtração. Isso causa:
- símbolos não processados por estarem pulados
- último caractere ignorado, pois
i
não atinge o valor esperado para adicioná-lo
Esse comportamento é conhecido como loop-and-a-half, causado pela tentativa de “escapar” do loop manualmente, quebrando o controle natural do for
.
Exemplo
Caso s = "IV"
i = 0
, detecta subtração, soma5 - 1
, fazi++
->i = 1
- final do loop,
for
faz maisi++
->i = 2
, loop encerra 'V'
foi processado pela subtração; bloco que adiciona o último símbolo é ignorado
Resultado esperado.
Caso s = III
i = 0
, detecta soma, adicionando “1” ao acumulador,i++
->i = 1
- final do loop,
for
faz maisi++
->i = 2
, loop encerra 'I'
foi interpretado como soma; porém o bloco que adiciona o último símbolo, caso não tenha sido processado, foi ignorado
Resultado errado.
Solução: loop simples
Substitua por:
int result = 0;
for (int i = 0; i < s.length(); i++) {
int curr = map.get(s.charAt(i));
if (i + 1 < s.length() && curr < map.get(s.charAt(i + 1))) {
result -= curr;
} else {
result += curr;
}
}
return result;
i
é incrementado apenas pelo loop- soma ou subtrai conforme a comparação
- processa todos os símbolos corretamente.
Alternativa: iteração reversa
Outra abordagem:
int total = 0, prev = 0;
for (int i = s.length() - 1; i >= 0; i--) {
int curr = map.get(s.charAt(i));
if (curr < prev) total -= curr;
else total += curr;
prev = curr;
}
return total;
- percorre de trás para frente
- elimina necessidade de pular índices
- evita o erro off-by-one
Aprendizados
- Não modificar manualmente o índice dentro do loop
- Usar loops com incremento controlado pelo próprio
for
- Testar edge cases simples (tamanho 1, 2, vazio)
- Usar intervalos semiabertos
0 ≤ i < n
- Revisar loops no papel para conferir limites (Baeldung)