Capítulo 15 — Loops com for
Série: Do zero ao jogo comercial com C++ e SFML
Quando sabemos onde a repetição começa, até onde ela vai e como o contador evolui, for reúne tudo em uma única estrutura
No capítulo anterior aprendemos a repetir ações com:
while
Nosso exemplo principal foi:
int day = 1;
const int TOTAL_DAYS = 5;
while (day <= TOTAL_DAYS)
{
std::cout << "Day " << day << std::endl;
day++;
}
E percebemos que esse pequeno loop possui três partes importantes:
INICIALIZAÇÃO
int day = 1;
CONDIÇÃO
day <= TOTAL_DAYS
ATUALIZAÇÃO
day++
No while, essas partes aparecem em locais diferentes.
Isso não é um problema.
Na verdade, while é excelente quando a repetição depende de uma condição cuja duração talvez não conheçamos antecipadamente.
Mas em nosso exemplo sabemos claramente:
começar em 1
↓
continuar até 5
↓
avançar de 1 em 1
Quando início, condição e atualização pertencem claramente à mesma repetição, C++ oferece uma estrutura muito conveniente:
for
Hoje não vamos apenas substituir while por uma nova sintaxe.
Vamos entender por que for existe, como ele funciona e quando ele comunica melhor nossa intenção.
1. O problema que o for ajuda a organizar
Relembre:
int day = 1;
while (day <= TOTAL_DAYS)
{
std::cout << "Day " << day << std::endl;
day++;
}
Temos:
int day = 1;
fora do loop.
Depois:
day <= TOTAL_DAYS
na condição.
E:
day++;
dentro do corpo.
Essas três partes controlam a mesma repetição.
Podemos desejar expressá-las juntas.
2. Conhecendo a estrutura do for
A forma fundamental é:
for (initialization; condition; update)
{
// código repetido
}
Observe três regiões:
for (
initialization;
condition;
update
)
Podemos ler:
inicialize o estado, continue enquanto a condição for verdadeira e atualize o estado após cada iteração.
3. Nosso primeiro for
O while:
int day = 1;
while (day <= TOTAL_DAYS)
{
std::cout << "Day " << day << std::endl;
day++;
}
pode ser expresso como:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
}
Programa completo:
#include <iostream>
int main()
{
const int TOTAL_DAYS = 5;
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
}
return 0;
}
Resultado:
Day 1
Day 2
Day 3
Day 4
Day 5
O comportamento é o mesmo.
A estrutura ficou diferente.
4. Vamos desmontar o cabeçalho
Temos:
for (int day = 1; day <= TOTAL_DAYS; day++)
Podemos visualizar:
for ( int day = 1 ; day <= TOTAL_DAYS ; day++ )
└────┬─────┘ └────────┬────────┘ └──┬──┘
│ │ │
inicialização condição atualização
Essas três partes pertencem ao controle do loop.
5. A inicialização
Primeiro:
int day = 1;
Essa parte acontece uma vez, quando o loop começa.
Temos:
day = 1
Não é executada novamente a cada iteração.
6. A condição
Depois:
day <= TOTAL_DAYS
é avaliada.
Se produzir:
true
o corpo executa.
Se produzir:
false
o loop termina.
Assim como no while, a condição é testada antes da execução do corpo.
7. O corpo
Quando:
day <= TOTAL_DAYS
é verdadeiro, executamos:
{
std::cout << "Day " << day << std::endl;
}
Essa é a ação repetida.
8. A atualização
Depois do corpo vem:
day++
Então:
1 → 2
2 → 3
3 → 4
4 → 5
5 → 6
Após a atualização, o programa volta para:
day <= TOTAL_DAYS
e avalia novamente.
9. O fluxo completo
Podemos visualizar:
INICIALIZAÇÃO
day = 1
↓
CONDIÇÃO
day <= TOTAL_DAYS?
│
├── false → sair do loop
│
└── true
↓
CORPO
imprimir day
↓
ATUALIZAÇÃO
day++
↓
voltar à condição
Esse ciclo continua até a condição produzir false.
10. A inicialização acontece apenas uma vez
Considere:
for (int day = 1; day <= 3; day++)
{
std::cout << "Day " << day << std::endl;
}
O fluxo não é:
day = 1
imprime 1
day = 1
imprime 1
day = 1
imprime 1
A inicialização acontece uma única vez.
Depois apenas:
condição
↓
corpo
↓
atualização
se repetem.
11. Simulando manualmente
Com:
const int TOTAL_DAYS = 3;
temos:
INICIALIZAÇÃO
day = 1
Primeira condição:
1 <= 3 → true
Saída:
Day 1
Atualização:
day = 2
Depois:
2 <= 3 → true
Saída:
Day 2
Atualização:
day = 3
Depois:
3 <= 3 → true
Saída:
Day 3
Atualização:
day = 4
Finalmente:
4 <= 3 → false
O loop termina.
12. for e while podem expressar a mesma repetição
Compare:
int day = 1;
while (day <= TOTAL_DAYS)
{
std::cout << "Day " << day << std::endl;
day++;
}
com:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
}
Os dois podem resolver o mesmo problema.
Portanto:
fornão tornawhileobsoleto.
Eles expressam diferentes intenções com maior ou menor clareza dependendo do contexto.
13. Quando for costuma comunicar bem a intenção?
Imagine:
repita de 1 até 5
ou:
execute exatamente 10 vezes
ou:
percorra índices de 0 até determinado limite
Nesses casos temos claramente:
início
fim
passo
O for reúne essas informações no mesmo lugar.
14. Quando while continua muito natural?
Imagine:
enquanto o jogador estiver vivo
ou:
enquanto o jogo estiver executando
ou:
enquanto existir trabalho pendente
Talvez não exista uma quantidade fixa de repetições.
Nesse caso:
while (isRunning)
{
}
pode comunicar melhor a ideia.
A escolha deve partir do problema.
15. Não existe regra "sempre use for"
Evite pensar:
for é melhor que while
ou:
while é mais simples que for
A pergunta correta continua sendo:
qual estrutura expressa melhor a natureza desta repetição?
Nosso loop de dias possui:
contador
limite conhecido
passo conhecido
Então for é muito natural.
16. Contando de zero
Em programação encontraremos frequentemente loops começando em:
0
Por exemplo:
for (int index = 0; index < 5; index++)
{
std::cout << "Index: " << index << std::endl;
}
Resultado:
Index: 0
Index: 1
Index: 2
Index: 3
Index: 4
Observe:
0
1
2
3
4
São cinco iterações.
17. Por que não apareceu 5?
A condição é:
index < 5
Quando:
index = 4
temos:
4 < 5
→ true
Depois:
index = 5
Então:
5 < 5
→ false
Logo, o valor 5 não entra no corpo.
18. Contagem e índice não são a mesma coisa conceitualmente
No exemplo de dias:
for (int day = 1; day <= TOTAL_DAYS; day++)
faz sentido começar em:
1
porque queremos:
Day 1
Mas em muitos contextos técnicos teremos índices iniciando em:
0
Mais tarde isso será extremamente importante quando estudarmos:
arrays;
containers;
sprites;
tiles;
entidades;
buffers;
dados.
Ainda não precisamos aprofundar índices agora.
19. O operador de atualização pode mudar
Não somos obrigados a utilizar:
day++
Podemos avançar de dois em dois:
for (int value = 0; value <= 10; value += 2)
{
std::cout << value << std::endl;
}
Resultado:
0
2
4
6
8
10
Temos:
início = 0
fim = 10
passo = 2
20. O passo faz parte da regra
Observe:
value += 2
O loop não sabe conceitualmente que estamos percorrendo números pares.
Ele apenas aplica a atualização:
0 → 2 → 4 → 6 → 8 → 10
A sequência resulta da nossa regra de atualização.
21. Também podemos contar para trás
for (int countdown = 5; countdown >= 1; countdown--)
{
std::cout << countdown << std::endl;
}
Resultado:
5
4
3
2
1
Agora temos:
início:
5
condição:
countdown >= 1
atualização:
countdown--
O for não exige contagem crescente.
22. Uma pequena contagem regressiva
Programa:
#include <iostream>
int main()
{
for (int countdown = 5; countdown >= 1; countdown--)
{
std::cout << countdown << std::endl;
}
std::cout << "Start!" << std::endl;
return 0;
}
Resultado:
5
4
3
2
1
Start!
É apenas um exercício de repetição.
Ainda não estamos criando timers ou controle real de tempo.
23. Cuidado ao combinar direção e condição
Considere:
for (int countdown = 5; countdown >= 1; countdown++)
Temos um problema.
Começamos em:
5
A condição é:
countdown >= 1
e fazemos:
5 → 6 → 7 → 8...
A condição continua verdadeira.
Criamos um loop que não termina normalmente.
24. Loop infinito também pode acontecer com for
Não pense que for elimina loops infinitos.
Podemos escrever:
for (int value = 1; value > 0; value++)
{
}
dependendo dos limites e do comportamento numérico envolvido, a condição pode permanecer verdadeira por um tempo extremamente longo ou o código pode envolver outras consequências.
O princípio continua:
a atualização precisa mover o estado em direção ao término esperado.
25. Início, condição e atualização precisam trabalhar juntos
Para contar:
1 → 2 → 3 → 4 → 5
podemos ter:
for (int value = 1; value <= 5; value++)
As três partes concordam:
começa em 1
continua enquanto <= 5
cresce
Agora, para:
5 → 4 → 3 → 2 → 1
temos:
for (int value = 5; value >= 1; value--)
Também existe coerência:
começa em 5
continua enquanto >= 1
diminui
26. Off-by-one continua existindo
Considere:
for (int day = 1; day < TOTAL_DAYS; day++)
Com:
const int TOTAL_DAYS = 5;
temos:
Day 1
Day 2
Day 3
Day 4
O Day 5 não aparece.
Assim como no while, temos:
<
versus:
<=
alterando a fronteira.
27. Teste primeiro, último e primeiro inválido
Para:
for (int day = 1; day <= 5; day++)
pense:
primeiro valor: 1
último valor válido: 5
primeiro valor que encerra: 6
Então:
1 <= 5 → true
5 <= 5 → true
6 <= 5 → false
Esse raciocínio continua sendo uma excelente ferramenta.
28. A variável criada no for
Observe:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
}
Declaramos:
int day = 1
dentro da inicialização do for.
Isso significa que day está associado ao escopo daquela estrutura.
Ainda teremos um capítulo específico sobre escopo.
Então não aprofundaremos agora.
Por enquanto, nossa intenção é:
o contador pertence ao loop.
29. Isso melhora a organização
Compare:
int day = 1;
while (day <= TOTAL_DAYS)
{
std::cout << day << std::endl;
day++;
}
com:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << day << std::endl;
}
Na segunda versão, todas as informações de controle aparecem juntas:
day começa em 1
day vai até TOTAL_DAYS
day aumenta em 1
Isso pode facilitar bastante a leitura.
30. Não modifique o contador sem necessidade dentro do corpo
Considere:
for (int day = 1; day <= 5; day++)
{
std::cout << day << std::endl;
day++;
}
Agora temos duas atualizações:
uma no corpo
+
uma no cabeçalho
O contador evoluirá mais rapidamente.
Resultado:
1
3
5
Talvez isso fosse intencional.
Mas, na maioria dos loops simples, modificar manualmente o contador dentro do corpo pode tornar o fluxo mais difícil de entender.
31. Deixe o controle do contador claro
Prefira:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
}
em vez de espalhar mudanças em day pelo corpo.
O cabeçalho já comunica:
início
condição
passo
Use essa vantagem.
32. if também pode aparecer dentro do for
Podemos portar nosso evento especial do capítulo anterior:
#include <iostream>
int main()
{
const int TOTAL_DAYS = 5;
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
if (day == 3)
{
std::cout << "A special event happened." << std::endl;
}
}
return 0;
}
Resultado:
Day 1
Day 2
Day 3
A special event happened.
Day 4
Day 5
O comportamento continua claro.
33. for não substitui decisões
O loop responde:
devo repetir?
O if responde:
devo executar esta ação neste momento?
Dentro do loop:
repetição
↓
cada iteração
↓
decisão específica
São conceitos diferentes trabalhando juntos.
34. Comparando novamente while e for
Com while
int day = 1;
while (day <= TOTAL_DAYS)
{
std::cout << "Day " << day << std::endl;
day++;
}
Comunicação principal
repita enquanto a condição for verdadeira
Com for
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
}
Comunicação principal
repita utilizando este contador,
deste início,
até esta condição,
com este passo
A diferença está muito mais na intenção expressa do que em capacidade absoluta.
35. Um exemplo contextual: produção por dias
Podemos criar uma pequena simulação:
#include <iostream>
int main()
{
const int TOTAL_DAYS = 5;
const int RESOURCES_PER_DAY = 3;
int totalResources = 0;
for (int day = 1; day <= TOTAL_DAYS; day++)
{
totalResources += RESOURCES_PER_DAY;
std::cout << "Day: " << day << std::endl;
std::cout << "Total Resources: " << totalResources << std::endl;
}
return 0;
}
Resultado:
Day: 1
Total Resources: 3
Day: 2
Total Resources: 6
Day: 3
Total Resources: 9
Day: 4
Total Resources: 12
Day: 5
Total Resources: 15
Agora cada iteração altera outro estado além do contador.
36. O contador e o estado do sistema são coisas diferentes
Temos:
int day
como variável de controle da repetição.
E:
int totalResources
como parte do estado que estamos simulando.
Não confunda:
contador do loop
com:
estado do domínio
Embora ambos sejam variáveis, possuem responsabilidades diferentes.
37. Microdesafio — dez dias
Utilize:
const int TOTAL_DAYS = 10;
e produza:
Day 1
Day 2
...
Day 10
com um único for.
Uma possível solução:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
}
38. Microdesafio — números pares
Mostre:
2
4
6
8
10
Uma possível solução:
for (int value = 2; value <= 10; value += 2)
{
std::cout << value << std::endl;
}
Observe:
início = 2
fim = 10
passo = 2
39. Microdesafio — contagem regressiva
Mostre:
5
4
3
2
1
Uma possível solução:
for (int countdown = 5; countdown >= 1; countdown--)
{
std::cout << countdown << std::endl;
}
Pergunte:
por que usamos
>=e--juntos?
A resposta precisa fazer sentido com a direção da contagem.
40. Microdesafio — evento em um dia específico
Utilize:
const int TOTAL_DAYS = 7;
e mostre:
Special event.
somente no:
Day 5
Uma possível solução:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
if (day == 5)
{
std::cout << "Special event." << std::endl;
}
}
41. Erro proposital — passo na direção errada
Teste temporariamente:
for (int countdown = 5; countdown >= 1; countdown++)
{
std::cout << countdown << std::endl;
}
Observe o problema.
Interrompa a execução.
Depois analise:
estado inicial:
5
condição:
countdown >= 1
atualização:
countdown++
Estamos nos afastando de 1, não nos aproximando do término.
Corrija:
countdown--;
42. Erro proposital — uma iteração a menos
Teste:
for (int day = 1; day < 5; day++)
{
std::cout << "Day " << day << std::endl;
}
Resultado:
Day 1
Day 2
Day 3
Day 4
Agora altere:
day < 5
para:
day <= 5
e explique por que Day 5 passou a aparecer.
43. Erro proposital — contador começando errado
Teste:
for (int day = 0; day <= 5; day++)
{
std::cout << "Day " << day << std::endl;
}
Resultado começa:
Day 0
Se nossa regra é:
primeiro dia = 1
então a inicialização está incorreta para o domínio.
Novamente:
código válido ≠ regra correta
44. Código final do capítulo
Vamos manter uma pequena simulação de produção durante alguns dias:
#include <iostream>
int main()
{
const int TOTAL_DAYS = 5;
const int RESOURCES_PER_DAY = 3;
int totalResources = 0;
std::cout << "=== SIMULATION START ===" << std::endl;
for (int day = 1; day <= TOTAL_DAYS; day++)
{
totalResources += RESOURCES_PER_DAY;
std::cout << "Day " << day << std::endl;
std::cout << "Resources Produced Today: " << RESOURCES_PER_DAY << std::endl;
std::cout << "Total Resources: " << totalResources << std::endl;
if (day == 3)
{
std::cout << "A special event happened." << std::endl;
}
std::cout << std::endl;
}
std::cout << "Simulation finished." << std::endl;
std::cout << "Final Resources: " << totalResources << std::endl;
return 0;
}
Resultado esperado:
=== SIMULATION START ===
Day 1
Resources Produced Today: 3
Total Resources: 3
Day 2
Resources Produced Today: 3
Total Resources: 6
Day 3
Resources Produced Today: 3
Total Resources: 9
A special event happened.
Day 4
Resources Produced Today: 3
Total Resources: 12
Day 5
Resources Produced Today: 3
Total Resources: 15
Simulation finished.
Final Resources: 15
45. O que esse código já combina?
Temos:
constantes
em:
const int TOTAL_DAYS = 5;
const int RESOURCES_PER_DAY = 3;
Temos estado mutável:
int totalResources = 0;
Temos repetição:
for (...)
Temos atualização:
totalResources += RESOURCES_PER_DAY;
Temos decisão:
if (day == 3)
E temos saída para observar o comportamento.
Os conceitos anteriores estão começando a trabalhar juntos.
46. Ainda não é nossa simulação real de fazenda
Não temos:
Crop
Farm
Inventory
Calendar
Economy
Weather
Nem precisamos.
A produção de três recursos por dia é apenas um exemplo educacional para observar estado sendo transformado dentro de um loop.
Não devemos transformar essa simplificação em regra definitiva do jogo.
47. Validando os limites
Teste:
const int TOTAL_DAYS = 1;
Esperado:
1 iteração
Depois:
const int TOTAL_DAYS = 0;
Com:
for (int day = 1; day <= TOTAL_DAYS; day++)
a condição inicial será:
1 <= 0
→ false
Resultado:
0 iterações
Isso é coerente.
48. Testando a produção
Com:
TOTAL_DAYS = 5
RESOURCES_PER_DAY = 3
o resultado esperado é:
15
Podemos calcular manualmente:
5 × 3 = 15
Nosso loop deve produzir o mesmo resultado.
Estamos novamente comparando:
resultado esperado
versus
resultado obtido
49. Checklist de compreensão
Antes de avançar, tente responder:
Para que serve
for?Quais são as três partes do cabeçalho?
Quando a inicialização é executada?
Quando a condição é avaliada?
Quando a atualização acontece?
O corpo pode executar zero vezes?
Qual é a diferença conceitual entre
whileefor?Quando
forcostuma comunicar melhor a intenção?Quando
whilecontinua sendo mais natural?Como contar de dois em dois?
Como contar para trás?
Como um
forpode gerar um loop infinito?Por que
<e<=continuam importantes?O que é um erro off-by-one?
Por que não devemos modificar o contador em vários lugares sem necessidade?
Qual a diferença entre o contador e o estado do sistema?
Se consegue explicar isso com suas próprias palavras, podemos seguir.
50. Estrutura do projeto
Ainda permanecemos com:
CppGameEngine/
└── main.cpp
Nenhuma classe nova.
Nenhuma pasta nova.
Nenhum sistema novo.
O código está ficando mais interessante, mas ainda não existe um problema estrutural grande o suficiente para justificar uma arquitetura maior.
Isso começará a mudar nos próximos capítulos.
51. Git
Depois de validar:
git status
Adicione:
git add CppGameEngine/main.cpp
Ajuste o caminho se necessário.
Commit sugerido:
git commit -m "feat: add counted repetition with for loop"
Esse commit representa:
nosso programa agora possui uma forma clara de executar repetições controladas por início, condição e atualização.
52. Atualizando o CHANGELOG
Em [Unreleased]:
### Added
- Added counted repetition using `for`.
- Added day-based iteration with explicit initialization, condition, and update.
- Added increasing, decreasing, and stepped loop examples.
- Added resource production simulation across multiple iterations.
- Added boundary and off-by-one validation scenarios.
Depois:
git add CHANGELOG.md
git commit -m "docs: update changelog with for loop examples"
O ROADMAP técnico continua sem necessidade de alteração para este avanço isolado.
53. O que aprendemos?
No capítulo anterior tínhamos:
int day = 1;
while (day <= TOTAL_DAYS)
{
std::cout << "Day " << day << std::endl;
day++;
}
Agora podemos representar a mesma repetição como:
for (int day = 1; day <= TOTAL_DAYS; day++)
{
std::cout << "Day " << day << std::endl;
}
Aprendemos que for reúne:
inicialização
condição
atualização
em um único lugar.
Também aprendemos que:
a inicialização ocorre uma vez;
a condição é verificada antes de cada iteração;
a atualização ocorre depois do corpo;
forpode executar zero vezes;o passo pode ser diferente de
1;podemos contar para frente ou para trás;
direção do passo e condição precisam ser coerentes;
loops infinitos ainda podem existir;
erros off-by-one continuam possíveis;
fornão substituiwhile;a escolha entre ambos deve refletir a natureza da repetição;
decisões podem acontecer dentro de loops;
o contador do loop não é necessariamente o estado principal do sistema.
Conclusão
Agora nosso programa já possui duas formas fundamentais de repetição.
Podemos dizer:
ENQUANTO uma condição for verdadeira
com:
while
ou representar de maneira muito clara:
comece aqui
continue enquanto...
avance desta forma
com:
for
Nossa evolução até aqui ficou muito interessante:
variáveis
↓
tipos
↓
constantes
↓
operadores
↓
expressões
↓
decisões
↓
alternativas
↓
múltiplas opções
↓
estados nomeados
↓
loops
Já conseguimos construir pequenas simulações como:
Day 1
↓
atualizar recursos
Day 2
↓
atualizar recursos
Day 3
↓
atualizar recursos
↓
executar evento
...
Mas ainda existe uma diferença enorme entre:
um loop que executa cinco vezes
e:
um jogo que permanece executando,
recebe atualizações continuamente
e só termina quando alguma condição manda parar
E agora finalmente temos conhecimento suficiente para construir uma primeira aproximação desse comportamento.
Ainda será no console.
Ainda não teremos SFML.
Ainda não teremos janela.
Mas vamos criar algo extremamente importante:
nosso primeiro mini Game Loop.
Próximo capítulo
Capítulo 16 — Nosso primeiro mini Game Loop no console
Série: Do zero ao jogo comercial com C++ e SFML
No próximo artigo vamos reunir vários conceitos que já aprendemos:
variáveis
bool
constantes
operadores
expressões
if
enum class
while
para construir algo conceitualmente semelhante a:
while (isRunning)
{
// process
// update
// show state
}
Mas não começaremos dizendo:
todo jogo tem Game Loop, copie este código.
Vamos partir do problema.
Nosso programa atual:
começa
↓
executa uma simulação limitada
↓
termina
Um jogo precisa de algo mais próximo de:
começa
↓
repete atualizações
↓
estado muda
↓
observa condição de continuidade
↓
repete novamente
↓
encerra quando necessário
Será nossa primeira aproximação prática de uma das estruturas mais importantes de toda a programação de jogos.
Também começaremos a perceber um problema novo:
main.cpp está crescendo.
Esse problema ainda não será resolvido imediatamente.
Mas começará a preparar o terreno para os capítulos seguintes sobre:
funções, parâmetros, valores de retorno, escopo e lifetime.
Nosso programa já sabe repetir.
No próximo capítulo ele começará a se comportar, pela primeira vez, como uma pequena aplicação de jogo em execução contínua.
Comentários
Ainda não há comentários. Sê o primeiro a comentar!