Projeto Calculadora

O primeiro projeto: uma calculadora no terminal. A Ana digita um número, a operação e outro número, e o programa mostra a conta. Parece simples, e é por isso que é um ótimo começo: cabe num arquivo só e, mesmo assim, junta quase tudo o que o Nível 1 ensina. No caminho aparecem três problemas de verdade: a pessoa digita uma palavra no lugar do número, alguém divide por zero e o programa precisa repetir sem você rodar de novo.

Você vai praticar

O que vamos construir

A calculadora pronta, na máquina da Ana:

$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Digite sair para terminar.

Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 15

Primeiro número: 8
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.

Primeiro número: abc
Isso não é um número.

Primeiro número: 2,5
Operação (+ - * /): *
Segundo número: 4
2,5 * 4 = 10

Primeiro número: sair
Até logo! Contas feitas: 3.
  1. Dois números e a soma.
  2. A operação: um switch.
  3. Entrada segura e divisão por zero (TryParse e o método Calcular).
  4. Repetir até "sair" (while, break e continue).

Antes de começar

Você precisa ter visto Variáveis, Ler do Teclado, Interpolação e if e else; para as versões 2 a 4, switch, Métodos e while. O programa é um arquivo só, calculadora.cs, rodado com dotnet run calculadora.cs (veja Sobre os Projetos):

$ mkdir -p cs/calculadora
$ cd cs/calculadora

Versão 1 · Dois números e a soma

O começo de qualquer calculadora: perguntar dois números e somar. O Console.ReadLine() devolve sempre um texto (string), mesmo que a Ana digite 10. Para fazer conta, é preciso converter esse texto num número com double.Parse. Usamos double para aceitar números com vírgula, como 2,5.

Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
double primeiro = double.Parse(Console.ReadLine()!);

Console.Write("Segundo número: ");
double segundo = double.Parse(Console.ReadLine()!);

double soma = primeiro + segundo;
Console.WriteLine($"{primeiro} + {segundo} = {soma}");
LinhaO que faz
Console.Write("Primeiro número: ");Mostra a pergunta sem pular linha (Write, não WriteLine): o cursor fica logo depois dos dois-pontos, esperando a resposta.
Console.ReadLine()Espera a Ana digitar e apertar Enter, e devolve o que ela digitou, como texto.
!Diz ao compilador "confie em mim, isto não é null". O ReadLine devolve null quando a entrada acaba; sem o !, o compilador avisaria (CS8604). Na versão 3 trocamos isso por algo mais seguro.
double.Parse(…)Converte o texto "10" no número 10. Como a máquina da Ana está em português, a casa decimal é a vírgula: 2,5.
$"{primeiro} + {segundo} = {soma}"Uma interpolação: cada {…} é trocado pelo valor da variável.
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: 10
Segundo número: 5
10 + 5 = 15
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: 7,5
Segundo número: 2,25
7,5 + 2,25 = 9,75

Repare que 10.0 + 5.0 aparece como 15, sem o ,0: o C# não mostra casas decimais que são zero.

Missão (versão 1): além da soma, mostre a subtração no formato 10 - 5 = 5. A entrada da missão é 10 e 5 (as respostas não aparecem na saída, porque ninguém as digitou).

Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
double primeiro = double.Parse(Console.ReadLine()!);

Console.Write("Segundo número: ");
double segundo = double.Parse(Console.ReadLine()!);

double soma = primeiro + segundo;
Console.WriteLine($"{primeiro} + {segundo} = {soma}");
// mostre também a subtração
Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
double primeiro = double.Parse(Console.ReadLine()!);

Console.Write("Segundo número: ");
double segundo = double.Parse(Console.ReadLine()!);

double soma = primeiro + segundo;
Console.WriteLine($"{primeiro} + {segundo} = {soma}");
Console.WriteLine($"{primeiro} - {segundo} = {primeiro - segundo}");

Versão 2 · A operação: um switch

Agora a Ana escolhe a conta. Entre os dois números, o programa pergunta a operação e guarda o texto ("+", "-", "*" ou "/"). Para escolher o que fazer com cada um, o switch é mais limpo do que quatro if seguidos.

Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
double primeiro = double.Parse(Console.ReadLine()!);

Console.Write("Operação (+ - * /): ");
string operacao = Console.ReadLine()!;

Console.Write("Segundo número: ");
double segundo = double.Parse(Console.ReadLine()!);

switch (operacao)
{
    case "+":
        Console.WriteLine($"{primeiro} + {segundo} = {primeiro + segundo}");
        break;
    case "-":
        Console.WriteLine($"{primeiro} - {segundo} = {primeiro - segundo}");
        break;
    case "*":
        Console.WriteLine($"{primeiro} * {segundo} = {primeiro * segundo}");
        break;
    case "/":
        Console.WriteLine($"{primeiro} / {segundo} = {primeiro / segundo}");
        break;
    default:
        Console.WriteLine($"Operação desconhecida: {operacao}");
        break;
}
string operacao = Console.ReadLine()!;
A operação fica como texto: não há nada para converter.
switch (operacao)
"Conforme o valor de operacao…". O C# compara o texto com cada case, de cima para baixo.
case "+":
Se operacao for "+", roda as linhas até o break.
break;
Sai do switch. Em C#, todo case com código tem de terminar com break (ou return); veja o cenário abaixo.
default:
"Nenhum dos anteriores": a Ana digitou outra coisa, como % ou x.
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 3
10 / 3 = 3,3333333333333335
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): %
Segundo número: 3
Operação desconhecida: %

O 3,3333333333333335 tem um 5 no fim, e não 3: o double guarda os números em base 2 e não consegue representar 10/3 exatamente (veja float, double e decimal). Para uma calculadora, tudo bem; para dinheiro, use decimal.

Esqueci um break: erro CS0163

A Ana apagou sem querer o break do case "+":

$ dotnet run calculadora.cs
/home/ana/cs/calculadora/calculadora.cs(14,5): error CS0163: Controle não pode passar através de um rótulo case ("case "+":") para outro

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: na linha 14 está o case "+":. Sem o break, o programa "cairia" do case "+" para dentro do case "-" e faria as duas contas (em inglês, Control cannot fall through from one case label to another). Em C e Java isso é permitido e causa defeitos; o C# proíbe.

Como consertar: termine cada case com break;.

10 dividido por 0 dá ∞
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 0
10 / 0 = ∞

O que aconteceu: com double, dividir por zero não é erro: o resultado é o valor especial "infinito", que em português aparece como ∞. (Com int ou decimal, a mesma conta lançaria DivideByZeroException.) Para uma calculadora, ∞ não é uma resposta útil. A versão 3 conserta.

"dez" no lugar de 10: FormatException
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: dez
Unhandled exception. System.FormatException: The input string 'dez' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Double.Parse(String s)
   at Program.<Main>$(String[] args) in /home/ana/cs/calculadora/calculadora.cs:line 4
$ echo $?
134

O que significa: "o texto 'dez' não está num formato correto". O double.Parse da linha 4 não sabe ler "dez", lança uma exceção, ninguém a trata e o programa cai com o código 134. Uma calculadora que cai com um erro de digitação não serve. A versão 3 também conserta isso.

Missão (versão 2): a calculadora já soma, subtrai e multiplica. Acrescente o case "/" para a divisão (não esqueça o break). A entrada da missão é 10, / e 4.

Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
double primeiro = double.Parse(Console.ReadLine()!);

Console.Write("Operação (+ - * /): ");
string operacao = Console.ReadLine()!;

Console.Write("Segundo número: ");
double segundo = double.Parse(Console.ReadLine()!);

switch (operacao)
{
    case "+":
        Console.WriteLine($"{primeiro} + {segundo} = {primeiro + segundo}");
        break;
    case "-":
        Console.WriteLine($"{primeiro} - {segundo} = {primeiro - segundo}");
        break;
    case "*":
        Console.WriteLine($"{primeiro} * {segundo} = {primeiro * segundo}");
        break;
    // acrescente aqui o case "/"
    default:
        Console.WriteLine($"Operação desconhecida: {operacao}");
        break;
}
Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
double primeiro = double.Parse(Console.ReadLine()!);

Console.Write("Operação (+ - * /): ");
string operacao = Console.ReadLine()!;

Console.Write("Segundo número: ");
double segundo = double.Parse(Console.ReadLine()!);

switch (operacao)
{
    case "+":
        Console.WriteLine($"{primeiro} + {segundo} = {primeiro + segundo}");
        break;
    case "-":
        Console.WriteLine($"{primeiro} - {segundo} = {primeiro - segundo}");
        break;
    case "*":
        Console.WriteLine($"{primeiro} * {segundo} = {primeiro * segundo}");
        break;
    case "/":
        Console.WriteLine($"{primeiro} / {segundo} = {primeiro / segundo}");
        break;
    default:
        Console.WriteLine($"Operação desconhecida: {operacao}");
        break;
}

Versão 3 · Entrada segura e divisão por zero

Duas mudanças. Primeiro, o double.Parse vira double.TryParse ("tentar converter"): em vez de cair, ele devolve false quando o texto não é um número, e o programa avisa a Ana. Segundo, a conta vai para um método Calcular, que recebe os dois números e a operação e devolve o texto do resultado. Lá dentro fica fácil tratar a divisão por zero.

Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
if (!double.TryParse(Console.ReadLine(), out double primeiro))
{
    Console.WriteLine("Isso não é um número.");
    return;
}

Console.Write("Operação (+ - * /): ");
string operacao = Console.ReadLine() ?? "";

Console.Write("Segundo número: ");
if (!double.TryParse(Console.ReadLine(), out double segundo))
{
    Console.WriteLine("Isso não é um número.");
    return;
}

Console.WriteLine(Calcular(primeiro, operacao, segundo));

static string Calcular(double a, string operacao, double b)
{
    switch (operacao)
    {
        case "+":
            return $"{a} + {b} = {a + b}";
        case "-":
            return $"{a} - {b} = {a - b}";
        case "*":
            return $"{a} * {b} = {a * b}";
        case "/":
            if (b == 0)
            {
                return "Não dá para dividir por zero.";
            }
            return $"{a} / {b} = {a / b}";
        default:
            return $"Operação desconhecida: {operacao}";
    }
}
double.TryParse(Console.ReadLine(), out double primeiro)
Tenta converter o texto. Se der certo, devolve true e guarda o número na variável nova primeiro (o out, veja ref, out e in). Se não der, devolve false. O TryParse aceita null, por isso o ! sumiu.
if (!…) { …; return; }
O ! na frente é o "não" (operadores lógicos): "se não conseguiu converter". O return termina o programa ali mesmo.
Console.ReadLine() ?? ""
Se a entrada acabou e veio null, usa um texto vazio (o operador ??, veja Operadores ??, ?. e ??=). O texto vazio cai no default.
static string Calcular(double a, string operacao, double b)
Um método que devolve (return) um string. Os nomes a e b são só dele: o primeiro chega em a, o segundo em b (Parâmetros).
case "+": return …;
Com return, o break não é necessário: o return já sai do método inteiro.
if (b == 0) return "Não dá para dividir por zero.";
Confere antes de dividir. Nada de ∞.
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: dez
Isso não é um número.
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): *
Segundo número: 3
10 * 3 = 30
Por que um método que devolve texto?

O programa principal ficou curto: perguntar, perguntar, perguntar, mostrar. Toda a regra das contas mora em Calcular, e ele não lê nem escreve nada no console: recebe valores e devolve um resultado. Um método assim é fácil de testar sozinho (veja Testes) e de reaproveitar, como você vai ver na versão 4.

Missão (versão 3): a divisão ainda dá 10 / 0 = ∞. No case "/" do método Calcular, antes do return da divisão, confira if (b == 0) e devolva "Não dá para dividir por zero.". A entrada da missão é 10, / e 0.

Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
if (!double.TryParse(Console.ReadLine(), out double primeiro))
{
    Console.WriteLine("Isso não é um número.");
    return;
}

Console.Write("Operação (+ - * /): ");
string operacao = Console.ReadLine() ?? "";

Console.Write("Segundo número: ");
if (!double.TryParse(Console.ReadLine(), out double segundo))
{
    Console.WriteLine("Isso não é um número.");
    return;
}

Console.WriteLine(Calcular(primeiro, operacao, segundo));

static string Calcular(double a, string operacao, double b)
{
    switch (operacao)
    {
        case "+":
            return $"{a} + {b} = {a + b}";
        case "-":
            return $"{a} - {b} = {a - b}";
        case "*":
            return $"{a} * {b} = {a * b}";
        case "/":
            // confira aqui se b é zero
            return $"{a} / {b} = {a / b}";
        default:
            return $"Operação desconhecida: {operacao}";
    }
}
Console.WriteLine("=== Calculadora da Ana ===");

Console.Write("Primeiro número: ");
if (!double.TryParse(Console.ReadLine(), out double primeiro))
{
    Console.WriteLine("Isso não é um número.");
    return;
}

Console.Write("Operação (+ - * /): ");
string operacao = Console.ReadLine() ?? "";

Console.Write("Segundo número: ");
if (!double.TryParse(Console.ReadLine(), out double segundo))
{
    Console.WriteLine("Isso não é um número.");
    return;
}

Console.WriteLine(Calcular(primeiro, operacao, segundo));

static string Calcular(double a, string operacao, double b)
{
    switch (operacao)
    {
        case "+":
            return $"{a} + {b} = {a + b}";
        case "-":
            return $"{a} - {b} = {a - b}";
        case "*":
            return $"{a} * {b} = {a * b}";
        case "/":
            if (b == 0)
            {
                return "Não dá para dividir por zero.";
            }
            return $"{a} / {b} = {a / b}";
        default:
            return $"Operação desconhecida: {operacao}";
    }
}

Versão 4 · Repetir até "sair"

Rodar o programa de novo para cada conta é cansativo. Na versão final, tudo fica dentro de um while (true), um laço "para sempre", e a Ana sai digitando sair no lugar do primeiro número. Quando ela erra um número, o programa não termina mais: avisa e pula para a próxima conta. No fim, mostra quantas contas foram feitas.

Console.WriteLine("=== Calculadora da Ana ===");
Console.WriteLine("Digite sair para terminar.");

int contas = 0;
while (true)
{
    Console.WriteLine();
    Console.Write("Primeiro número: ");
    string entrada = Console.ReadLine() ?? "sair";
    if (entrada == "sair")
    {
        break;
    }
    if (!double.TryParse(entrada, out double primeiro))
    {
        Console.WriteLine("Isso não é um número.");
        continue;
    }

    Console.Write("Operação (+ - * /): ");
    string operacao = Console.ReadLine() ?? "sair";

    Console.Write("Segundo número: ");
    if (!double.TryParse(Console.ReadLine(), out double segundo))
    {
        Console.WriteLine("Isso não é um número.");
        continue;
    }

    Console.WriteLine(Calcular(primeiro, operacao, segundo));
    contas++;
}

Console.WriteLine($"Até logo! Contas feitas: {contas}.");

static string Calcular(double a, string operacao, double b)
{
    switch (operacao)
    {
        case "+":
            return $"{a} + {b} = {a + b}";
        case "-":
            return $"{a} - {b} = {a - b}";
        case "*":
            return $"{a} * {b} = {a * b}";
        case "/":
            if (b == 0)
            {
                return "Não dá para dividir por zero.";
            }
            return $"{a} / {b} = {a / b}";
        default:
            return $"Operação desconhecida: {operacao}";
    }
}
while (true)
Um laço sem fim: a condição é sempre verdadeira. Quem decide a hora de parar é o break lá dentro (veja while).
Console.WriteLine();
Uma linha em branco entre uma conta e outra, para a tela ficar legível.
string entrada = Console.ReadLine() ?? "sair";
Primeiro guardamos o texto, porque ele pode ser "sair" ou um número. Se a entrada acabar (null, por exemplo com Ctrl+D no terminal), o ?? faz de conta que a Ana digitou sair. Sem isso, o laço pediria números para sempre.
break;
Sai do while. O programa continua na linha depois do laço, o "Até logo!".
continue;
Pula o resto da volta e começa a próxima: a Ana errou o número, então pedimos tudo de novo (break e continue).
contas++;
Soma 1 no contador a cada conta que chegou ao Calcular (++ e --).
$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Digite sair para terminar.

Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 15

Primeiro número: 7
Operação (+ - * /): %
Segundo número: 2
Operação desconhecida: %

Primeiro número: sair
Até logo! Contas feitas: 2.

A conta com % também entrou no contador: ela chegou ao Calcular, que respondeu "Operação desconhecida". Contar só as contas que deram certo é o desafio 2.

Missão (versão 4): o laço já funciona, mas o fim sempre diz Contas feitas: 0.. Conte cada conta com contas++; logo depois do Console.WriteLine(Calcular(…)). A entrada da missão faz duas contas (10 + 5 e 8 * 2) e depois sair.

Console.WriteLine("=== Calculadora da Ana ===");
Console.WriteLine("Digite sair para terminar.");

int contas = 0;
while (true)
{
    Console.WriteLine();
    Console.Write("Primeiro número: ");
    string entrada = Console.ReadLine() ?? "sair";
    if (entrada == "sair")
    {
        break;
    }
    if (!double.TryParse(entrada, out double primeiro))
    {
        Console.WriteLine("Isso não é um número.");
        continue;
    }

    Console.Write("Operação (+ - * /): ");
    string operacao = Console.ReadLine() ?? "sair";

    Console.Write("Segundo número: ");
    if (!double.TryParse(Console.ReadLine(), out double segundo))
    {
        Console.WriteLine("Isso não é um número.");
        continue;
    }

    Console.WriteLine(Calcular(primeiro, operacao, segundo));
    // conte a conta
}

Console.WriteLine($"Até logo! Contas feitas: {contas}.");

static string Calcular(double a, string operacao, double b)
{
    switch (operacao)
    {
        case "+":
            return $"{a} + {b} = {a + b}";
        case "-":
            return $"{a} - {b} = {a - b}";
        case "*":
            return $"{a} * {b} = {a * b}";
        case "/":
            if (b == 0)
            {
                return "Não dá para dividir por zero.";
            }
            return $"{a} / {b} = {a / b}";
        default:
            return $"Operação desconhecida: {operacao}";
    }
}
Console.WriteLine("=== Calculadora da Ana ===");
Console.WriteLine("Digite sair para terminar.");

int contas = 0;
while (true)
{
    Console.WriteLine();
    Console.Write("Primeiro número: ");
    string entrada = Console.ReadLine() ?? "sair";
    if (entrada == "sair")
    {
        break;
    }
    if (!double.TryParse(entrada, out double primeiro))
    {
        Console.WriteLine("Isso não é um número.");
        continue;
    }

    Console.Write("Operação (+ - * /): ");
    string operacao = Console.ReadLine() ?? "sair";

    Console.Write("Segundo número: ");
    if (!double.TryParse(Console.ReadLine(), out double segundo))
    {
        Console.WriteLine("Isso não é um número.");
        continue;
    }

    Console.WriteLine(Calcular(primeiro, operacao, segundo));
    contas++;
}

Console.WriteLine($"Até logo! Contas feitas: {contas}.");

static string Calcular(double a, string operacao, double b)
{
    switch (operacao)
    {
        case "+":
            return $"{a} + {b} = {a + b}";
        case "-":
            return $"{a} - {b} = {a - b}";
        case "*":
            return $"{a} * {b} = {a * b}";
        case "/":
            if (b == 0)
            {
                return "Não dá para dividir por zero.";
            }
            return $"{a} / {b} = {a / b}";
        default:
            return $"Operação desconhecida: {operacao}";
    }
}
"10.5" virou 105

A Ana testou um número com ponto, como nas calculadoras de celular em inglês:

$ dotnet run calculadora.cs
=== Calculadora da Ana ===
Digite sair para terminar.

Primeiro número: 10.5
Operação (+ - * /): +
Segundo número: 1
105 + 1 = 106

Primeiro número: sair
Até logo! Contas feitas: 1.

O que aconteceu: em português, o ponto separa os milhares (1.000), e o TryParse leu 10.5 como "cento e cinco". Não é um erro do programa: ele seguiu a cultura da máquina. Na tela, porém, o 105 mostra o que aconteceu, e a Ana percebeu. Como consertar: diga na pergunta qual separador usar ("use vírgula"), ou veja o desafio 4.


O programa completo

A versão final é a versão 4, acima. O que o projeto juntou: Console.ReadLine, TryParse, o operador ??, switch, um método com valor de retorno, while, break e continue e um contador com ++.

Rodando com a entrada de um arquivo

Para testar várias contas de uma vez, a Ana guardou as respostas em contas.txt, uma por linha, e mandou o arquivo para o programa com < (veja Sobre os Projetos). As respostas não aparecem, porque ninguém as digitou:

$ cat contas.txt
10
+
5
sair
$ dotnet run calculadora.cs < contas.txt
=== Calculadora da Ana ===
Digite sair para terminar.

Primeiro número: Operação (+ - * /): Segundo número: 10 + 5 = 15

Primeiro número: Até logo! Contas feitas: 1.

Desafios extras

  1. Mais operações. Acrescente % (o resto da divisão) e ^ (potência, com Math.Pow(a, b); veja Math).
  2. Só as contas certas. Faça o contador subir só quando a conta deu certo. Dica: o Calcular pode devolver também um bool, numa tupla: (bool Ok, string Texto).
  3. Expressão switch. Reescreva o Calcular com uma expressão switch: operacao switch { "+" => …, _ => … }.
  4. Ponto ou vírgula. Antes de converter, troque o ponto por vírgula com entrada.Replace('.', ',') (veja Métodos de String). Que problema isso cria para quem digita 1.000?
  5. Uma linha só. Leia a conta inteira numa linha, como 10 + 5, e separe as partes com Split(' ').

Exercícios

1. O que Console.ReadLine() devolve quando a Ana digita 10 e aperta Enter?

2. Com double, o que 10.0 / 0 mostra na máquina da Ana?

3. Dentro do while (true), o que o continue faz?

4. Por que o C# dá o erro CS0163 quando falta o break num case?

5. Complete a conversão que não lança exceção:

if (double.(Console.ReadLine(), out double numero))

6. Complete o operador que usa "sair" quando o ReadLine devolve null:

string entrada = Console.ReadLine() "sair";

Resumo

  • Console.ReadLine() devolve texto; para fazer conta, converta com double.Parse ou, melhor, double.TryParse.
  • Parse com texto errado lança FormatException e o programa cai (código 134); TryParse devolve false e você decide o que fazer.
  • O switch escolhe pela operação; cada case termina com break ou return (senão, CS0163).
  • Com double, dividir por zero dá ∞; confira antes de dividir.
  • Um método que recebe valores e devolve o resultado deixa o programa principal curto e a regra fácil de testar.
  • while (true) com break e continue faz o menu "até sair"; ?? "sair" evita o laço infinito quando a entrada acaba.

Próximo projeto: Jogo de Adivinhação.