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.

Você vai aprender
  • 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 \n no 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 = 1 roda 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

linhacoluna passa porDepois
11, 2, 3, 4 (e 5 faz coluna <= 4 falhar)\n; linha vira 2
21, 2, 3, 4 (recomeçou do 1)\n; linha vira 3
31, 2, 3, 4\n; linha vira 4 e o laço de fora termina
Nomes que ajudam

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.
Use o 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

Só aparece uma linha (o mesmo contador nos dois laços)
#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; …).

A tela enche de estrelas (passo da variável errada)
#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++).

O desenho sai em pé (\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 programa fica lento

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

FormaO 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:

for (int l = 1; l <= 4; l++) { for (int c = 1; ; c++) printf("*"); printf("\n"); }

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 \n depois do de dentro.
  • Quando o de dentro usa a variável do de fora (c <= l), sai um triângulo.
  • O break sai 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 for usando a mesma variável.