C Laços Aninhados
O corpo de um laço pode ter qualquer coisa: printf, if, contas… e também outro laço. Um laço dentro de outro se chama laço aninhado (como um ninho, um dentro do outro; em inglês, nested loop). É assim que se percorre tudo o que tem linhas e colunas: uma tabuada inteira, um tabuleiro, uma planilha de notas, um desenho feito de caracteres.
- Como um laço dentro de outro roda: o de dentro dá todas as voltas a cada volta do de fora.
- Como montar a tabuada completa em forma de tabela.
- Como desenhar um retângulo e um triângulo de
*. - Como sair dos dois laços de uma vez: com uma bandeira ou com
goto. - Os erros clássicos: o mesmo contador nos dois laços, o passo trocado e o
\nno lugar errado.
Esta aula usa o for e o break (de break e continue). Todos os programas foram compilados com o gcc 15 na máquina fictícia da Ana, na pasta ~/c, com o comando padrão: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.
A ideia: o relógio
Olhe um relógio digital. Os minutos vão de 00 a 59; quando completam a volta, a hora anda uma vez e os minutos recomeçam do 00. Para a hora avançar de 8 para 9, os minutos precisam dar todas as 60 voltas.
Laços aninhados funcionam do mesmo jeito:
- o laço de fora é a hora: anda devagar;
- o laço de dentro são os minutos: dá todas as voltas dele, recomeça do início e só então o de fora anda um passo.
Se o de fora dá 3 voltas e o de dentro dá 4, o corpo do de dentro roda 3 × 4 = 12 vezes.
1. Linhas e colunas
#include <stdio.h>
int main(void) {
for (int linha = 1; linha <= 3; linha++) {
for (int coluna = 1; coluna <= 4; coluna++) {
printf("(%d,%d) ", linha, coluna);
}
printf("\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra grade.c -o grade $ ./grade (1,1) (1,2) (1,3) (1,4) (2,1) (2,2) (2,3) (2,4) (3,1) (3,2) (3,3) (3,4)
- for (int linha = 1; linha <= 3; linha++)
- O laço de fora: 3 voltas, uma por linha.
- for (int coluna = 1; coluna <= 4; coluna++)
- O laço de dentro, que é o corpo do de fora. A cada volta do de fora, ele recomeça:
int coluna = 1roda de novo, e as 4 colunas são percorridas. - printf("(%d,%d) ", linha, coluna);
- O corpo do de dentro: roda 3 × 4 = 12 vezes. Ele enxerga as duas variáveis.
- printf("\n");
- Está fora do laço de dentro, mas dentro do de fora: roda 3 vezes, uma no fim de cada linha. É ele que faz a quebra de linha.
A ordem das voltas
linha | coluna passa por | Depois |
|---|---|---|
| 1 | 1, 2, 3, 4 (e 5 faz coluna <= 4 falhar) | \n; linha vira 2 |
| 2 | 1, 2, 3, 4 (recomeçou do 1) | \n; linha vira 3 |
| 3 | 1, 2, 3, 4 | \n; linha vira 4 e o laço de fora termina |
Em laços aninhados, nomes como linha e coluna (ou l e c) evitam confusão. A tradição dos livros é i para o de fora e j para o de dentro; só não use o mesmo nome nos dois (veja Quando dá errado).
2. A tabuada completa
Na aula do for, um laço mostrava a tabuada de um número. Com um laço por fora, saem todas, do 1 ao 10, como uma tabela: a linha é um número, a coluna é o outro, e cada célula é a multiplicação dos dois.
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
printf("%4d", i * j);
}
printf("\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra tabuada_toda.c -o tabuada_toda $ ./tabuada_toda 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100
O %4d reserva 4 espaços para cada número, alinhado à direita, por isso as colunas ficam retas mesmo com números de 1, 2 ou 3 algarismos (veja Formatos do printf). Para achar 7 × 8, vá até a linha 7, coluna 8: 56. Foram 100 multiplicações com 7 linhas de código.
3. Desenhar com *: o retângulo
Trocando o número por um caractere, os laços viram um "pincel". O de fora desenha as linhas (altura); o de dentro, os caracteres de cada linha (largura):
#include <stdio.h>
int main(void) {
int largura = 8;
int altura = 4;
for (int l = 1; l <= altura; l++) {
for (int c = 1; c <= largura; c++) {
printf("*");
}
printf("\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra retangulo.c -o retangulo $ ./retangulo ******** ******** ******** ********
Missão: desenhe um retângulo de 5 de largura por 3 de altura. Depois, por conta própria, troque o * por # ou por "[]" e veja o desenho mudar.
#include <stdio.h>
int main(void) {
int largura = 8;
int altura = 4;
for (int l = 1; l <= altura; l++) {
for (int c = 1; c <= largura; c++) {
printf("*");
}
printf("\n");
}
return 0;
}
#include <stdio.h>
int main(void) {
int largura = 5;
int altura = 3;
for (int l = 1; l <= altura; l++) {
for (int c = 1; c <= largura; c++) {
printf("*");
}
printf("\n");
}
return 0;
}
4. O triângulo: o de dentro depende do de fora
No retângulo, toda linha tinha o mesmo tamanho. Num triângulo, a linha 1 tem 1 estrela, a linha 2 tem 2, a linha 3 tem 3… O truque é usar a variável do laço de fora na condição do laço de dentro:
#include <stdio.h>
int main(void) {
int altura = 5;
for (int l = 1; l <= altura; l++) {
for (int c = 1; c <= l; c++) {
printf("*");
}
printf("\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra triangulo.c -o triangulo $ ./triangulo * ** *** **** *****
- c <= l
- O laço de dentro vai até o número da linha atual. Na linha 1, roda 1 vez; na linha 5, roda 5 vezes. Total: 1 + 2 + 3 + 4 + 5 = 15 estrelas.
Esse padrão, "o de dentro vai até o de fora", aparece em muitos lugares: comparar cada aluno com os seguintes, ordenar uma lista, imprimir só metade de uma tabela.
Missão: vire o triângulo de cabeça para baixo: a primeira linha com 5 estrelas, a última com 1. Dica: faça o laço de fora contar para trás (de 5 até 1) e deixe o de dentro como está.
#include <stdio.h>
int main(void) {
int altura = 5;
for (int l = 1; l <= altura; l++) {
for (int c = 1; c <= l; c++) {
printf("*");
}
printf("\n");
}
return 0;
}
#include <stdio.h>
int main(void) {
int altura = 5;
for (int l = altura; l >= 1; l--) {
for (int c = 1; c <= l; c++) {
printf("*");
}
printf("\n");
}
return 0;
}
5. Sair dos dois laços de uma vez
Como você viu em break e continue, o break só sai do laço mais de dentro. Às vezes isso não basta. A Ana procura dois números que multiplicados deem 36 e somados deem 13. Assim que achar o par, não há por que continuar procurando, nem no laço de dentro nem no de fora.
Jeito 1: uma bandeira
#include <stdio.h>
int main(void) {
int achou = 0;
for (int a = 1; a <= 20 && !achou; a++) {
for (int b = a; b <= 20; b++) {
if (a * b == 36 && a + b == 13) {
printf("Achei: %d e %d\n", a, b);
achou = 1;
break;
}
}
}
if (!achou) {
printf("Não existe esse par.\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra par_flag.c -o par_flag $ ./par_flag Achei: 4 e 9
- int b = a
- O de dentro começa do valor do de fora: assim o par 4 e 9 não é testado de novo como 9 e 4.
- achou = 1; break;
- Levanta a bandeira e sai do laço de dentro.
- a <= 20 && !achou
- O laço de fora também testa a bandeira (
!achou= "não achou"). Com ela levantada, a condição fica falsa e o de fora também termina.
Jeito 2: goto
O C tem um comando que pula para qualquer ponto marcado da mesma função: o goto ("vá para"). Para sair de vários laços de uma vez, ele é curto e direto:
#include <stdio.h>
int main(void) {
for (int a = 1; a <= 20; a++) {
for (int b = a; b <= 20; b++) {
if (a * b == 36 && a + b == 13) {
printf("Achei: %d e %d\n", a, b);
goto fim;
}
}
}
printf("Não existe esse par.\n");
fim:
printf("Busca encerrada.\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra par_goto.c -o par_goto $ ./par_goto Achei: 4 e 9 Busca encerrada.
- fim:
- Um rótulo (em inglês, label): um nome seguido de
:que marca uma linha. - goto fim;
- Pula direto para o rótulo, saindo dos dois laços. O
Não existe esse par.fica para trás, sem rodar.
goto com moderação
Um programa cheio de goto pulando para cima e para baixo fica impossível de seguir (o famoso "código espaguete"). Sair de laços aninhados é um dos poucos usos aceitos, junto com a limpeza de recursos no fim de uma função. Os detalhes, e quando evitar, estão na aula goto. Na dúvida, prefira a bandeira, ou coloque os laços numa função e use return para sair de tudo.
Formas diferentes de escrever
Misturar tipos de laço
Nada obriga os dois laços a serem for. Um menu (do while) pode ter dentro um for que lista os produtos; um while que lê arquivos pode ter dentro um for que percorre cada linha. A regra é a mesma: o de dentro roda inteiro a cada volta do de fora.
Mais de dois níveis
Dá para aninhar três, quatro laços (por exemplo, dia → hora → minuto). Mas cada nível multiplica o número de voltas: 3 laços de 1000 voltas são 1 bilhão de voltas. E o código fica difícil de ler. Com mais de dois ou três níveis, separe o laço de dentro numa função com um nome que explique o que ela faz.
Quando dá errado
#include <stdio.h>
int main(void) {
int i;
for (i = 1; i <= 3; i++) {
for (i = 1; i <= 4; i++) {
printf("*");
}
printf("\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra mesmo_i.c -o mesmo_i $ ./mesmo_i ****
O que aconteceu: os dois laços usam a mesma variável i, declarada fora (no estilo antigo). O de dentro termina com i valendo 5; o de fora faz i++ (6), testa 6 <= 3, e para. Eram esperadas 3 linhas. Com outros limites, o mesmo erro pode virar um laço infinito. O compilador não avisa.
Como consertar: uma variável para cada laço, de preferência criada no próprio for: for (int l = 1; …) e for (int c = 1; …).
#include <stdio.h>
int main(void) {
for (int l = 1; l <= 3; l++) {
for (int c = 1; c <= 4; l++) {
printf("*");
}
printf("\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra troca.c -o troca $ ./troca **************************************************************************************** ^C « a linha de estrelas não acabava; a Ana apertou Ctrl+C
O que aconteceu: o laço de dentro testa c, mas o passo soma em l (um erro de digitação comum quando os nomes são parecidos, como i e j). Como c nunca muda, c <= 4 é verdadeiro para sempre. No editor do guia, o programa é interrompido ao passar de 2 MB de saída.
Como consertar: confira se as três partes de cada for usam a mesma variável: for (int c = 1; c <= 4; c++).
\n no laço errado)
#include <stdio.h>
int main(void) {
for (int l = 1; l <= 2; l++) {
for (int c = 1; c <= 3; c++) {
printf("*\n");
}
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra vertical.c -o vertical $ ./vertical * * * * * *
O que aconteceu: o esperado eram 2 linhas de 3 estrelas. O \n está no laço de dentro, então cada estrela ganha a sua própria linha.
Como consertar: o laço de dentro escreve só o caractere (printf("*");); a quebra de linha (printf("\n");) vai depois dele, no corpo do de fora.
O que aconteceu: laços aninhados multiplicam as voltas. Dois laços de 100 mil voltas são 10 bilhões de voltas: o programa pode levar minutos, e no editor do guia ele é parado depois de 15 segundos.
Como investigar: multiplique os limites dos laços para estimar o total. Se for grande demais, procure um jeito de sair cedo (break quando achar) ou um método que não precise testar todas as combinações.
Referência
| Forma | O que faz |
|---|---|
for (l…) { for (c…) { … } printf("\n"); } | Percorre linhas e colunas. O corpo de dentro roda (voltas de fora) × (voltas de dentro) vezes. |
for (int c = 1; c <= l; c++) | O de dentro depende do de fora: triângulo. |
printf("%4d", x) | Número em 4 espaços, para alinhar colunas. |
for (…; … && !achou; …) + achou = 1; break; | Sair dos dois laços com bandeira. |
goto fim; … fim: | Sair de vários laços de uma vez (use pouco; veja goto). |
Exercícios
1. Quantas vezes?
Quantas vezes printf("*") roda em for (int i = 0; i < 4; i++) for (int j = 0; j < 6; j++) printf("*");?
2. A condição do triângulo
Complete para o laço de dentro desenhar l estrelas na linha l:
3. Quantas estrelas no total?
No triângulo do exercício 2 (4 linhas), quantas estrelas são desenhadas?
4. Num laço aninhado, o que acontece com o laço de dentro a cada volta do de fora?
5. Um break no laço de dentro…
6. Um retângulo de * saiu com uma estrela por linha. Qual é o erro mais provável?
Missão final: as tabuadas do 1 ao 3
Missão: o programa mostra só a tabuada do 2. Coloque um laço por fora para mostrar as tabuadas do 1 ao 3, cada uma com o título Tabuada do N e as linhas de N x 1 até N x 5.
#include <stdio.h>
int main(void) {
int n = 2;
printf("Tabuada do %d\n", n);
for (int i = 1; i <= 5; i++) {
printf("%d x %d = %d\n", n, i, n * i);
}
return 0;
}
#include <stdio.h>
int main(void) {
for (int n = 1; n <= 3; n++) {
printf("Tabuada do %d\n", n);
for (int i = 1; i <= 5; i++) {
printf("%d x %d = %d\n", n, i, n * i);
}
}
return 0;
}
Resumo
- Um laço pode ter outro laço no corpo: é um laço aninhado. O de dentro dá todas as voltas a cada volta do de fora.
- Total de voltas do corpo de dentro = voltas de fora × voltas de dentro.
- Linhas no de fora, colunas no de dentro, e o
\ndepois do de dentro. - Quando o de dentro usa a variável do de fora (
c <= l), sai um triângulo. - O
breaksai só de um laço; para sair de dois, use uma bandeira ou, com moderação,goto. - Uma variável diferente para cada laço, e as três partes de cada
forusando a mesma variável.