C# Laços Aninhados
Com um laço, a Ana já sabe repetir uma linha: cinco asteriscos, a tabuada do 7, as notas de um aluno. Mas muita coisa no mundo vem em linhas e colunas: a tabuada inteira, um tabuleiro de xadrez, os assentos de um cinema, os dias de um calendário. Para isso, a ferramenta é colocar um laço dentro de outro. Isso se chama laço aninhado (em inglês, nested loop: "laço no ninho", um dentro do outro, como as bonecas russas). Não há nenhuma palavra nova nesta aula: só o for, o while e o foreach que você já conhece, combinados.
- Como um
fordentro de outroforfunciona, volta por volta. - A diferença entre
Console.WriteeConsole.WriteLinena hora de desenhar linhas e colunas. - Como desenhar retângulos e triângulos, montar a tabuada em grade e listar combinações.
- Quantas vezes o laço de dentro roda (e por que isso cresce tão rápido).
- Por que o
breaksó sai do laço de dentro, e três jeitos de sair dos dois. - Os erros mais comuns: o mesmo nome nos dois laços (
CS0136), usar a variável de dentro fora dele (CS0103), a quebra de linha no lugar errado e o++na variável errada.
Todos os programas foram rodados com o .NET 10 (dotnet --version = 10.0.112, C# 14) na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cs, com dotnet run arquivo.cs (aula Rodar com dotnet run). Antes desta aula, é bom ter visto o for e o break e continue.
A ideia: o relógio de ponteiros
Olhe um relógio. O ponteiro dos minutos dá uma volta inteira, de 0 a 59, e só então o ponteiro das horas anda um passo. Aí os minutos começam tudo de novo, do zero. Para as horas andarem de 1 até 3, os minutos dão três voltas completas.
- O ponteiro das horas é o laço de fora: anda devagar, um passo de cada vez.
- O ponteiro dos minutos é o laço de dentro: a cada passo do de fora, ele dá uma volta inteira, do começo ao fim.
Essa é a regra de ouro desta aula: para cada volta do laço de fora, o laço de dentro roda do começo ao fim.
1. O primeiro laço aninhado
Para ver a ordem das voltas, o programa abaixo só mostra o valor das duas variáveis em cada passo:
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 2; j++)
{
Console.WriteLine($"i = {i}, j = {j}");
}
}
- for (int i = 1; i <= 3; i++)
- O laço de fora, o ponteiro das horas:
ivale 1, 2 e 3. - { … }
- O corpo do laço de fora. Tudo o que está entre essas chaves se repete 3 vezes, incluindo o outro
for. - for (int j = 1; j <= 2; j++)
- O laço de dentro, o ponteiro dos minutos. Cada vez que o programa chega nesta linha, ele cria um
jnovinho, que começa em 1 e vai até 2. - Console.WriteLine($"i = {i}, j = {j}");
- Mostra as duas variáveis (o
$"…{…}"é a interpolação da aula Interpolação). O de dentro enxerga oido de fora sem problema nenhum. - i e j
- São os nomes tradicionais:
ipara o laço de fora,jpara o de dentro (ekpara um terceiro). Quando os laços têm um sentido, prefira nomes que digam o que são:linhaecoluna, como nos próximos exemplos.
$ dotnet run voltas.cs i = 1, j = 1 i = 1, j = 2 « o de dentro deu uma volta inteira com i = 1 i = 2, j = 1 « o de fora andou um passo; o j recomeçou do 1 i = 2, j = 2 i = 3, j = 1 i = 3, j = 2
Repare: o i só muda depois que o j termina. E o j recomeça do 1 todas as vezes. O laço de fora deu 3 voltas, o de dentro deu 2 voltas a cada uma delas, e a linha do meio rodou 3 × 2 = 6 vezes.
Passo a passo
| Passo | O que acontece | Saída |
|---|---|---|
| 1 | i = 1. Chega no for de dentro: cria j = 1. 1 <= 2? Sim. | i = 1, j = 1 |
| 2 | j++ → 2. 2 <= 2? Sim. | i = 1, j = 2 |
| 3 | j++ → 3. 3 <= 2? Não: o de dentro termina. Volta para o de fora. | — |
| 4 | i++ → 2. 2 <= 3? Sim. Chega no for de dentro de novo: um j novo, valendo 1. | i = 2, j = 1 |
| … | O mesmo para i = 2 e i = 3. | … |
| fim | i++ → 4. 4 <= 3? Não: o de fora termina, e o programa acaba. | — |
2. Linhas e colunas: Write e WriteLine
Para desenhar, a gente precisa de duas formas de escrever (aula Saída):
Console.Write("*")("escrever") põe o asterisco e fica na mesma linha: o próximo texto sai colado nele.Console.WriteLine()("escrever linha"), sem nada dentro dos parênteses, só pula para a linha de baixo.
Com isso, a receita de qualquer desenho é: o laço de fora cuida das linhas; o de dentro, das colunas de cada linha; e, quando o de dentro termina, uma quebra de linha.
for (int linha = 1; linha <= 3; linha++)
{
for (int coluna = 1; coluna <= 5; coluna++)
{
Console.Write("*");
}
Console.WriteLine();
}
| Linha do código | O que faz |
|---|---|
for (int linha = 1; linha <= 3; linha++) | Repete 3 vezes: uma por linha do desenho. |
for (int coluna = 1; coluna <= 5; coluna++) | Em cada linha, repete 5 vezes: uma por coluna. |
Console.Write("*"); | Desenha um asterisco e continua na mesma linha. Roda 3 × 5 = 15 vezes. |
Console.WriteLine(); | Fica fora do laço de dentro e dentro do de fora: roda uma vez por linha, depois dos 5 asteriscos, e pula para baixo. |
$ dotnet run retangulo.cs ***** ***** *****
Num laço aninhado, a mesma linha de código faz coisas bem diferentes conforme a chave em que ela está. Antes de escrever, pergunte: "isto acontece uma vez por coluna (dentro do de dentro), uma vez por linha (depois do de dentro, ainda no de fora) ou uma vez só (fora de tudo)?". A seção "Quando dá errado" mostra o que sai com o WriteLine no lugar errado.
Missão: o programa desenha uma linha de 6 asteriscos. Coloque tudo dentro de outro for para desenhar 4 linhas de 6 asteriscos, um retângulo.
****** ****** ****** ******
for (int coluna = 1; coluna <= 6; coluna++)
{
Console.Write("*");
}
Console.WriteLine();
for (int linha = 1; linha <= 4; linha++)
{
for (int coluna = 1; coluna <= 6; coluna++)
{
Console.Write("*");
}
Console.WriteLine();
}
3. O laço de dentro pode depender do de fora
No retângulo, toda linha tinha 5 colunas. Mas a condição do laço de dentro pode usar a variável do de fora. Troque o coluna <= 5 por coluna <= linha: a linha 1 ganha 1 asterisco, a linha 2 ganha 2, e assim por diante.
for (int linha = 1; linha <= 5; linha++)
{
for (int coluna = 1; coluna <= linha; coluna++)
{
Console.Write("*");
}
Console.WriteLine();
}
$ dotnet run triangulo.cs * ** *** **** *****
- coluna <= linha
- O "tamanho" do laço de dentro muda a cada volta do de fora. Com
linha = 1, o de dentro dá 1 volta; comlinha = 5, dá 5. No total, 1 + 2 + 3 + 4 + 5 = 15 asteriscos.
Missão: vire o triângulo de cabeça para baixo: a primeira linha com 5 asteriscos e a última com 1. Dica: mude só a condição do laço de dentro. Quantos asteriscos a linha 1 tem de ter? E a linha 5?
***** **** *** ** *
for (int linha = 1; linha <= 5; linha++)
{
for (int coluna = 1; coluna <= linha; coluna++)
{
Console.Write("*");
}
Console.WriteLine();
}
for (int linha = 1; linha <= 5; linha++)
{
for (int coluna = 1; coluna <= 6 - linha; coluna++)
{
Console.Write("*");
}
Console.WriteLine();
}
Outra solução que funciona: deixar o de dentro como está e fazer o de fora andar para trás, for (int linha = 5; linha >= 1; linha--).
4. A tabuada em grade
Na aula for, a Ana mostrou a tabuada de um número. Com dois laços, ela monta a grade inteira: o de fora escolhe a linha, o de dentro a coluna, e cada casa é linha * coluna.
for (int linha = 1; linha <= 5; linha++)
{
for (int coluna = 1; coluna <= 5; coluna++)
{
Console.Write($"{linha * coluna,4}");
}
Console.WriteLine();
}
- linha * coluna
- A conta de cada casa: na linha 3, coluna 4, sai 12.
- {…,4}
- O
,4dentro das chaves da interpolação é o alinhamento: o número ocupa 4 espaços, encostado à direita. Sem ele, o5e o25teriam larguras diferentes e as colunas ficariam tortas (mais em Formatar Números).
$ dotnet run tabuada.cs 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25
Experimente: aumente a tabuada para 10 × 10 (troque os dois 5 por 10). Depois tente {linha * coluna,3} e {linha * coluna,6} e veja as colunas mudarem de largura. O que acontece com {linha * coluna}, sem o ,4?
for (int linha = 1; linha <= 5; linha++)
{
for (int coluna = 1; coluna <= 5; coluna++)
{
Console.Write($"{linha * coluna,4}");
}
Console.WriteLine();
}
5. Combinações: tudo com tudo
Laços aninhados também servem para listar todas as combinações de duas coisas. A Ana quer saber quantas senhas de 2 letras dá para fazer só com A, B e C. Um char também pode ser a variável de um for, porque cada letra é um número por baixo e o ++ passa para a próxima (aula char e bool):
int total = 0;
for (char primeira = 'A'; primeira <= 'C'; primeira++)
{
for (char segunda = 'A'; segunda <= 'C'; segunda++)
{
Console.Write($"{primeira}{segunda} ");
total++;
}
Console.WriteLine();
}
Console.WriteLine($"Total: {total} senhas");
$ dotnet run senhas.cs AA AB AC BA BB BC CA CB CC Total: 9 senhas
- int total = 0;
- Fica fora dos dois laços: é criado uma vez só e vai somando. Se ficasse dentro do de fora, voltaria a zero a cada linha.
- char primeira = 'A'; primeira <= 'C'; primeira++
- A primeira letra: A, B e C.
- $"{primeira}{segunda} "
- As duas letras coladas, e um espaço para separar da próxima senha.
- total++;
- Conta uma senha por volta do laço de dentro: 3 × 3 = 9.
6. Quantas vezes o de dentro roda?
A conta é sempre a mesma: voltas do de fora × voltas do de dentro. Com laços pequenos, isso nem se nota. Com laços grandes, cresce muito rápido:
int voltasDeFora = 0;
int voltasDeDentro = 0;
for (int i = 0; i < 1000; i++)
{
voltasDeFora++;
for (int j = 0; j < 1000; j++)
{
voltasDeDentro++;
}
}
Console.WriteLine($"O laço de fora rodou {voltasDeFora} vezes.");
Console.WriteLine($"O de dentro rodou {voltasDeDentro} vezes.");
$ dotnet run contar.cs O laço de fora rodou 1000 vezes. O de dentro rodou 1000000 vezes. « mil vezes mil: um milhão
Um milhão de voltas ainda é rápido para o computador (uma fração de segundo). Mas três laços de 1000, um dentro do outro, já dariam um bilhão. Quando um programa com laços aninhados fica lento, a primeira coisa a fazer é multiplicar as voltas e ver se dá para fazer menos.
7. break só sai do laço de dentro
A Ana quer descobrir dois números de 1 a 9 cuja soma é 10 e cujo produto é 24. Ela testa todas as duplas e usa o break (aula break e continue) para parar quando achar:
// Dois números de 1 a 9 com soma 10 e produto 24
for (int a = 1; a <= 9; a++)
{
for (int b = 1; b <= 9; b++)
{
if (a + b == 10 && a * b == 24)
{
Console.WriteLine($"Achei: {a} e {b}");
break;
}
}
}
Console.WriteLine("Fim da busca");
$ dotnet run busca.cs Achei: 4 e 6 Achei: 6 e 4 « ué: o break não parou tudo? Fim da busca
O break sai só do laço em que ele está, o mais de dentro. Quando achou 4 e 6, ele parou o laço do b, mas o laço do a continuou: 5, 6 (e achou 6 e 4), 7, 8, 9. O C# não tem um "break dos dois laços". Há três jeitos de resolver.
Jeito 1: uma variável bool de aviso
bool achou = false;
for (int a = 1; a <= 9 && !achou; a++)
{
for (int b = 1; b <= 9; b++)
{
if (a + b == 10 && a * b == 24)
{
Console.WriteLine($"Achei: {a} e {b}");
achou = true;
break;
}
}
}
Console.WriteLine("Fim da busca");
- bool achou = false;
- Uma "bandeira" (em inglês, flag), criada fora dos dois laços: "ainda não achei".
- a <= 9 && !achou
- O de fora só continua se ainda houver números e se ainda não achou (
!é o "não", aula Operadores Lógicos). - achou = true; break;
- Levanta a bandeira e sai do de dentro. O de fora confere a condição, vê
!achoufalso e termina também.
$ dotnet run busca2.cs Achei: 4 e 6 Fim da busca
Jeito 2: goto
O C# tem o goto ("vá para"), que pula direto para um rótulo: um nome seguido de dois-pontos, como fim:. Sair de laços aninhados é um dos poucos usos aceitos dele:
for (int a = 1; a <= 9; a++)
{
for (int b = 1; b <= 9; b++)
{
if (a + b == 10 && a * b == 24)
{
Console.WriteLine($"Achei: {a} e {b}");
goto fim;
}
}
}
fim:
Console.WriteLine("Fim da busca");
$ dotnet run busca3.cs Achei: 4 e 6 Fim da busca
Funciona, mas o goto deixa o caminho do programa difícil de seguir quando é usado à vontade, e muita gente o evita. Use só para isto, e com um rótulo de nome claro.
Jeito 3: um método com return
O jeito mais limpo é colocar a busca num método (uma função) e usar return, que sai do método inteiro, com todos os laços de uma vez. Você vai ver isso em Métodos e Valor de Retorno.
Missão: este é o busca.cs, que acha a resposta duas vezes. Faça ele parar na primeira: a saída tem de ser só Achei: 4 e 6 e Fim da busca. Use a bandeira bool ou o goto.
Achei: 4 e 6 Fim da busca
for (int a = 1; a <= 9; a++)
{
for (int b = 1; b <= 9; b++)
{
if (a + b == 10 && a * b == 24)
{
Console.WriteLine($"Achei: {a} e {b}");
break;
}
}
}
Console.WriteLine("Fim da busca");
bool achou = false;
for (int a = 1; a <= 9 && !achou; a++)
{
for (int b = 1; b <= 9; b++)
{
if (a + b == 10 && a * b == 24)
{
Console.WriteLine($"Achei: {a} e {b}");
achou = true;
break;
}
}
}
Console.WriteLine("Fim da busca");
E o continue?
Com o continue vale a mesma regra: ele pula para a próxima volta do laço em que está. Aqui, ele pula as casas em que a linha e a coluna são iguais (a "diagonal"):
for (int linha = 1; linha <= 3; linha++)
{
for (int coluna = 1; coluna <= 3; coluna++)
{
if (linha == coluna)
{
continue;
}
Console.Write($"({linha},{coluna}) ");
}
Console.WriteLine();
}
$ dotnet run continue.cs (1,2) (1,3) (2,1) (2,3) (3,1) (3,2) « (1,1), (2,2) e (3,3) foram puladas
O continue pulou só o resto daquela volta do laço de dentro. O Console.WriteLine() do de fora rodou normalmente nas três linhas.
8. Misturando laços diferentes
Qualquer laço pode ficar dentro de qualquer outro: for dentro de foreach, while dentro de for, do while dentro de while. Aqui, um foreach passa pelas letras de um texto e um while repete cada letra 3 vezes:
foreach (char letra in "ANA")
{
int vezes = 0;
while (vezes < 3)
{
Console.Write(letra);
vezes++;
}
Console.Write(' ');
}
Console.WriteLine();
$ dotnet run letras.cs AAA NNN AAA
- foreach (char letra in "ANA")
- O de fora:
letravale'A','N'e'A'. - int vezes = 0;
- Fica dentro do de fora, antes do
while: volta a zero para cada letra. Noforisso acontece sozinho (oint j = 0faz parte dele); nowhile, quem zera é você. Se esta linha ficasse lá em cima, fora de tudo, só a primeira letra seria repetida. - Console.Write(' ');
- Um espaço depois de cada grupo, uma vez por letra.
Quantos níveis?
Dá para pôr um terceiro laço dentro do segundo, e um quarto dentro do terceiro. Mas, a partir de três, o código fica difícil de ler, e a quantidade de voltas explode (seção 6). Se você precisar de muitos níveis, é sinal de que um pedaço deveria virar um método separado. Para guardar dados em linhas e colunas, existe o array de duas dimensões, que você vai ver em Arrays Multidimensionais, quase sempre percorrido com dois for aninhados, exatamente como nesta aula.
Quando dá errado
error CS0136: o mesmo nome nos dois laços
for (int i = 1; i <= 3; i++)
{
for (int i = 1; i <= 2; i++)
{
Console.WriteLine(i);
}
}
$ dotnet run mesmo-nome.cs /home/ana/cs/mesmo-nome.cs(3,14): error CS0136: Um local ou um parâmetro denominado "i" não pode ser declarado neste escopo porque esse nome é usado em um escopo delimitador de local para definir um local ou parâmetro Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: "uma variável local chamada i não pode ser criada aqui, porque esse nome já é usado num escopo de fora". O laço de dentro está dentro do corpo do de fora, onde o i já existe. Se o C# deixasse, você não teria como falar do i de fora. Em inglês: A local or parameter named 'i' cannot be declared in this scope because that name is used in an enclosing local scope. A posição (3,14) é linha 3, coluna 14: o segundo int i.
Como consertar: dê um nome diferente à variável de dentro: j, ou melhor, um nome com sentido (linha e coluna). Dois laços um depois do outro (não um dentro do outro) podem usar o mesmo i à vontade: cada um tem o seu (aula Escopo).
error CS0103: usar a variável do laço de dentro fora dele
for (int linha = 1; linha <= 2; linha++)
{
for (int coluna = 1; coluna <= 3; coluna++)
{
Console.Write("*");
}
Console.WriteLine($" ({coluna} colunas)");
}
$ dotnet run fora.cs /home/ana/cs/fora.cs(7,28): error CS0103: O nome "coluna" não existe no contexto atual Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: "o nome coluna não existe aqui" (The name 'coluna' does not exist in the current context). A variável criada no for só vive dentro dele. Na linha 7, o laço de dentro já terminou e o coluna sumiu. O contrário funciona: o de dentro enxerga o linha do de fora.
Como consertar: se você precisa do valor depois do laço, guarde-o numa variável criada antes dele (por exemplo, int colunas = 3; lá em cima, usada no for e no WriteLine).
Console.WriteLine()
for (int linha = 1; linha <= 3; linha++)
{
for (int coluna = 1; coluna <= 5; coluna++)
{
Console.Write("*");
}
}
$ dotnet run sem-quebra.cs ***************$ « 15 asteriscos colados, e até o prompt grudou neles
O que significa: o programa desenhou as 3 linhas, mas nada mandou pular de linha. O computador não sabe que você "queria" um retângulo.
Como consertar: coloque Console.WriteLine(); depois da chave que fecha o laço de dentro, ainda dentro do de fora.
Console.WriteLine() dentro do laço errado
for (int linha = 1; linha <= 2; linha++)
{
for (int coluna = 1; coluna <= 3; coluna++)
{
Console.Write("*");
Console.WriteLine();
}
}
$ dotnet run quebra-dentro.cs * * * * * *
O que significa: o WriteLine ficou dentro do laço de dentro e rodou a cada coluna, 2 × 3 = 6 vezes.
Como consertar: passe-o para baixo da chave } do laço de dentro. Uma boa indentação (os 4 espaços de cada nível) ajuda a ver onde cada linha está.
++ na variável errada
for (int linha = 1; linha <= 3; linha++)
{
for (int coluna = 1; coluna <= 5; linha++)
{
Console.Write("*");
}
Console.WriteLine();
}
$ dotnet run i-errado.cs ********************************************************************************* ********************************************************************************* ********************************************************************************* « a tela se enche sem parar ^C « a Ana aperta Ctrl+C $ echo $? 130
O que significa: o laço de dentro faz linha++ em vez de coluna++. O coluna fica em 1 para sempre, 1 <= 5 é sempre verdade, e o laço nunca termina: um laço infinito. O compilador não avisa nada, porque o código é válido. O Ctrl+C interrompe o programa, e o código de saída 130 quer dizer "interrompido pelo Ctrl+C".
Como consertar: confira as três partes de cada for: a variável que é criada, a que é testada e a que anda têm de ser a mesma. Copiar e colar um for para fazer o de dentro é a origem mais comum deste erro.
Referência rápida
| Situação | Como fica |
|---|---|
| Laço dentro de laço | for (…) { for (…) { … } }: para cada volta do de fora, o de dentro roda inteiro |
| Quantas vezes o de dentro roda | voltas do de fora × voltas do de dentro (3 × 5 = 15) |
| Desenhar linhas e colunas | de fora = linhas; de dentro = colunas com Console.Write; Console.WriteLine() depois do de dentro |
| Triângulo | a condição de dentro usa a variável de fora: coluna <= linha |
| Colunas alinhadas | $"{valor,4}": ocupa 4 espaços, encostado à direita |
| Nomes | i, j, k, ou nomes com sentido (linha, coluna); nunca o mesmo nome nos dois (CS0136) |
break / continue | Valem só para o laço em que estão (o mais de dentro) |
| Sair dos dois laços | bandeira bool na condição do de fora, goto rotulo;, ou return dentro de um método |
| Contador que zera a cada linha | Crie-o dentro do de fora, antes do de dentro |
| Contador do total | Crie-o fora dos dois laços |
Exercícios
1. Quantas vezes?
Quantas vezes o Console.Write("*") roda neste código?
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
Console.Write("*");
}
}
2. O triângulo
Complete a condição do laço de dentro para cada linha ter tantos asteriscos quanto o seu número (linha 1: *; linha 2: **…):
3. Pular de linha
Que comando, colocado logo depois do laço de dentro, faz o desenho pular para a próxima linha?
4. Num laço aninhado, o break que está dentro do laço de dentro…
5. O que este código mostra?
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
Console.Write($"{i}{j} ");
6. O compilador mostrou error CS0136: Um local ou um parâmetro denominado "i" não pode ser declarado neste escopo. O que aconteceu?
Missão final: o tabuleiro de xadrez
Missão: o programa desenha 4 linhas de 8 casas, todas #. Faça as casas alternarem entre # (escura) e . (clara), como num tabuleiro. Dica: some a linha e a coluna. Quando a soma é par ((linha + coluna) % 2 == 0), a casa é escura; quando é ímpar, é clara.
#.#.#.#. .#.#.#.# #.#.#.#. .#.#.#.#
for (int linha = 0; linha < 4; linha++)
{
for (int coluna = 0; coluna < 8; coluna++)
{
Console.Write('#');
}
Console.WriteLine();
}
for (int linha = 0; linha < 4; linha++)
{
for (int coluna = 0; coluna < 8; coluna++)
{
if ((linha + coluna) % 2 == 0)
{
Console.Write('#');
}
else
{
Console.Write('.');
}
}
Console.WriteLine();
}
Por que a soma funciona? Andar uma casa para o lado ou para baixo muda a soma em 1, e ela passa de par para ímpar (ou o contrário): é exatamente o que faz a cor trocar. A casa (0, 0) tem soma 0, par, e começa escura. Com o operador ternário (aula Operador Ternário), o if inteiro cabe numa linha: Console.Write((linha + coluna) % 2 == 0 ? '#' : '.');.
Resumo
- Um laço aninhado é um laço dentro do corpo de outro. Para cada volta do de fora, o de dentro roda do começo ao fim, como os ponteiros de um relógio.
- O corpo do de dentro roda voltas de fora × voltas de dentro vezes. Isso cresce rápido: 1000 × 1000 = um milhão.
- Para desenhar, o de fora cuida das linhas, o de dentro das colunas (
Console.Write), e umConsole.WriteLine()fecha cada linha, depois do de dentro. - A condição do de dentro pode usar a variável do de fora (
coluna <= linha): é assim que se faz um triângulo. - Cada laço precisa de uma variável com nome próprio (
CS0136), e a variável do de dentro não existe fora dele (CS0103). breakecontinuevalem só para o laço mais de dentro. Para sair dos dois, use uma bandeirabool, umgotoou umreturnnum método.- Qualquer laço pode ficar dentro de qualquer outro (
foreach,while,for). Nowhile, lembre de zerar o contador do de dentro a cada volta do de fora.