Projeto Calculadora
O primeiro projeto do guia: uma calculadora que roda no terminal. Ela pede um número, a operação e outro número, mostra o resultado e pergunta se você quer fazer outra conta. Parece simples, e é de propósito: a conta é a parte fácil. O desafio é fazer o programa não quebrar quando alguém divide por zero, escolhe uma operação que não existe ou digita "abc" no lugar de um número.
O que vamos construir
Esta é a calculadora pronta, rodando na máquina da Ana. Tudo o que aparece depois dos dois-pontos nas perguntas foi digitado por ela:
$ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * /): + Segundo número: 5 10 + 5 = 15 Outra conta? (s/n): s Primeiro número: seis "seis" não é um número. Tente de novo. « texto no lugar de número Primeiro número: 6 Operação (+ - * /): % Operação desconhecida: % « operação que não existe Operação (+ - * /): * Segundo número: 1.5 6 * 1.5 = 9 Outra conta? (s/n): s Primeiro número: 1 Operação (+ - * /): / Segundo número: 0 Não dá para dividir por zero. « divisão por zero Outra conta? (s/n): n Até logo!
Vamos chegar lá em seis etapas. No fim de cada uma, o programa compila e roda:
- Ler dois números e somar (
std::cin,double). - Escolher a operação (
chareswitch). - Não dividir por zero (
if). - Repetir até a pessoa sair (
do-while). - Validar o que foi digitado (
cinfalhando,clear,getline). - Organizar em funções.
Antes de começar
Você precisa saber criar, compilar e rodar um programa (Olá, Mundo e Compilar com g++), usar variáveis double e char e ter visto o básico do cin. No seu computador, crie a pasta do projeto; o arquivo vai se chamar calculadora.cpp. Como fazer cada etapa no editor ou no terminal está em Como Usar os Projetos.
$ mkdir -p cpp/calculadora $ cd cpp/calculadora
Etapa 1 · Ler dois números e somar
O começo de toda calculadora: pedir dois números e mostrar a soma. Os números são double (com casas decimais), porque a Ana quer fazer contas como 2.5 + 1.25.
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Segundo número: ";
std::cin >> b;
std::cout << a << " + " << b << " = " << a + b << '\n';
}
| Linha | O que faz |
|---|---|
#include <iostream> | Traz o std::cout (saída) e o std::cin (entrada). |
double a = 0.0; | Cria a variável do primeiro número, já com zero. Toda variável começa com um valor (veja Boas Práticas). |
std::cout << "Primeiro número: "; | Mostra a pergunta sem \n: o cursor fica na mesma linha, e o que a pessoa digitar aparece logo depois dos dois-pontos. |
std::cin >> a; | Espera a pessoa digitar e apertar Enter, converte o texto num double e guarda em a. O cin (de character input, entrada de caracteres) é o teclado. |
a << " + " << b << " = " << a + b | Monta a frase pedaço por pedaço: número, texto, número, texto e a soma. |
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora $ ./calculadora === Calculadora da Ana === Primeiro número: 10 Segundo número: 5 10 + 5 = 15 $ ./calculadora === Calculadora da Ana === Primeiro número: 2.5 Segundo número: 1.25 2.5 + 1.25 = 3.75
Repare: a Ana digitou 10, a variável é double, e o cout mostrou 10, sem .0. O cout só mostra as casas decimais que existem.
O cin lê números no formato do C++, com ponto: 2.5. Se a Ana digitar 2,5, veja o que acontece:
$ ./calculadora === Calculadora da Ana === Primeiro número: 2,5 Segundo número: 2 + 0 = 2
O cin >> a leu o 2 e parou na vírgula, que não faz parte de um número. A vírgula e o 5 ficaram esperando na entrada. O cin >> b tentou ler um número começando pela vírgula, falhou, e colocou 0 em b, sem nem esperar a Ana digitar. Na etapa 5 você vai aprender a perceber essa falha.
Missão (etapa 1): o programa já lê o primeiro número. Peça e leia o segundo número na variável b e mostre a soma no formato 10 + 5 = 15. A entrada 10 e 5 já vem preenchida (no editor, o que a entrada "digita" não aparece na tela, só as perguntas e a resposta).
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
std::cout << "Primeiro número: ";
std::cin >> a;
// peça e leia o segundo número
// mostre a soma no formato 10 + 5 = 15
}
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Segundo número: ";
std::cin >> b;
std::cout << a << " + " << b << " = " << a + b << '\n';
}
Etapa 2 · Escolher a operação
Agora a calculadora pergunta qual conta fazer. A operação é um único caractere (+, -, * ou /), então cabe num char (veja Caracteres). Para escolher a conta, um switch: ele compara o op com cada case e pula para o que combina.
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
resultado = a / b;
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
return 1;
}
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
- char op = ' ';
- Um caractere, começando com um espaço. Caracteres vão entre aspas simples:
'+'. Aspas duplas ("+") seriam um texto, e oswitchnão aceita texto. - switch (op)
- "Conforme o valor de
op…". - case '+':
- "…se for
'+', comece aqui." - break;
- "Terminei, saia do
switch." Sem ele, o programa continua nocaseseguinte (veja o cenário abaixo). - default:
- "Se não for nenhum dos anteriores." Aqui mostramos o erro e saímos com
return 1;: o código de saída 1 avisa ao sistema que algo deu errado (0 quer dizer "tudo certo"). - a << ' ' << op << ' ' << b
- Mostra a conta com a operação escolhida, com espaços (
' ', umchar) entre as partes.
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora $ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * /): / Segundo número: 4 10 / 4 = 2.5 $ ./calculadora === Calculadora da Ana === Primeiro número: 7 Operação (+ - * /): % Segundo número: 2 Operação desconhecida: % $ printf "7\n%%\n2\n" | ./calculadora > /dev/null; echo $? 1 « o código de saída do return 1
A última linha usa um truque do terminal: o printf "digita" as três respostas, a saída do programa é jogada fora (> /dev/null) e o echo $? mostra o código de saída. Não se preocupe com ele; é só para você ver o 1.
O cin >> separa os valores por espaços, não por linhas. Se a Ana digitar 10 * 3 na primeira pergunta, os três valores ficam esperando na entrada, e as perguntas seguintes nem param:
$ ./calculadora === Calculadora da Ana === Primeiro número: 10 * 3 Operação (+ - * /): Segundo número: 10 * 3 = 30
break: a conta "escorrega" para o próximo case
Este código tem um erro de propósito: o break depois da soma foi apagado.
case '+':
resultado = a + b;
case '-':
resultado = a - b;
break;
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
calculadora.cpp: In function ‘int main()’:
calculadora.cpp:19:23: warning: this statement may fall through [-Wimplicit-fallthrough=]
19 | resultado = a + b;
| ~~~~~~~~~~^~~~~~~
calculadora.cpp:20:9: note: here
20 | case '-':
| ^~~~
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 5
O que significa: "esta instrução pode cair para o próximo" (fall through, "cair através"). Sem o break, depois de somar o programa continuou no case '-' e trocou o resultado por 10 - 5. A conta "10 + 5 = 5" está errada, mas o programa rodou sem reclamar. Só o aviso do -Wextra denunciou o problema.
Como consertar: um break; no fim de cada case. Veja mais em switch.
Missão (etapa 2): esta versão só sabe somar e subtrair. Acrescente os case da multiplicação ('*') e da divisão ('/'), cada um com o seu break. A entrada da missão é 10, /, 4.
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
// acrescente aqui a multiplicação e a divisão
default:
std::cout << "Operação desconhecida: " << op << '\n';
return 1;
}
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
resultado = a / b;
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
return 1;
}
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
Etapa 3 · Não dividir por zero
O que acontece se a Ana dividir por zero? Na matemática, não existe resposta. No C++, com double, existe uma resposta estranha:
$ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * /): / Segundo número: 0 10 / 0 = inf $ ./calculadora === Calculadora da Ana === Primeiro número: -10 Operação (+ - * /): / Segundo número: 0 -10 / 0 = -inf $ ./calculadora === Calculadora da Ana === Primeiro número: 0 Operação (+ - * /): / Segundo número: 0 0 / 0 = -nan
inf é "infinito" (infinity) e nan é "não é um número" (not a number). São valores especiais do double, explicados em float e double. O programa não quebrou, mas uma calculadora que responde "infinito" não ajuda ninguém. Com int seria pior: dividir um inteiro por zero é comportamento indefinido e costuma derrubar o programa.
A solução é conferir antes de dividir. Só o case '/' muda:
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
return 1;
}
resultado = a / b;
break;
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora $ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * /): / Segundo número: 0 Não dá para dividir por zero. $ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * /): / Segundo número: 4 10 / 4 = 2.5
O return 1; dentro do case encerra o main na hora: a linha que mostra o resultado nem chega a rodar.
Missão (etapa 3): rode como está: com a entrada 10, /, 0, a calculadora responde inf. Acrescente no case '/' o if que mostra Não dá para dividir por zero. e encerra com return 1;.
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
resultado = a / b;
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
return 1;
}
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
return 1;
}
resultado = a / b;
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
return 1;
}
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
Etapa 4 · Repetir até a pessoa sair
Até agora, a calculadora faz uma conta e termina. Para fazer outra, a Ana precisa rodar o programa de novo. Vamos colocar tudo dentro de um laço que, no fim de cada conta, pergunta "Outra conta? (s/n)".
Qual laço? A conta precisa acontecer pelo menos uma vez, e a pergunta "continuar?" vem no fim. É exatamente o caso do do-while: "faça isto, e repita enquanto a resposta for s".
Há um detalhe: os return 1; das etapas anteriores encerravam o programa inteiro. Agora, um erro (operação desconhecida, divisão por zero) deve só pular a conta, não fechar a calculadora. Por isso entra uma variável deu_certo, que começa true e vira false quando algo dá errado.
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
char resposta = 'n';
do {
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
bool deu_certo = true;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
deu_certo = false;
} else {
resultado = a / b;
}
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
deu_certo = false;
}
if (deu_certo) {
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
} while (resposta == 's' || resposta == 'S');
std::cout << "Até logo!\n";
}
| Linha nova | O que faz |
|---|---|
char resposta = 'n'; | Guarda o s ou n. Fica fora do laço, porque o while lá no fim precisa enxergá-la (veja Escopo). |
do { | Começa o bloco que se repete. As variáveis a, b e op agora nascem dentro dele: a cada conta, começam do zero. |
bool deu_certo = true; | "Até agora, tudo certo." Os casos de erro mudam para false em vez de return 1. |
if (deu_certo) { … } | Só mostra o resultado quando a conta foi feita. |
} while (resposta == 's' || resposta == 'S'); | Volta para o do se a resposta for s ou S. Qualquer outra coisa encerra o laço. O ; no fim é obrigatório no do-while. |
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora $ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * /): + Segundo número: 5 10 + 5 = 15 Outra conta? (s/n): s Primeiro número: 7 Operação (+ - * /): / Segundo número: 0 Não dá para dividir por zero. « o erro não fechou a calculadora Outra conta? (s/n): s Primeiro número: 3 Operação (+ - * /): x Segundo número: 3 Operação desconhecida: x Outra conta? (s/n): n Até logo!
Missão (etapa 4): o código já tem o deu_certo e a pergunta "Outra conta?", mas ainda faz uma conta só. Coloque tudo, da criação de a até a leitura da resposta, dentro de um do { … } while (resposta == 's' || resposta == 'S');. A entrada da missão faz duas contas (10 + 5 e 3 * 4) e responde n.
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
char resposta = 'n';
// repita tudo daqui até o "Outra conta?" enquanto a resposta for s
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
bool deu_certo = true;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
deu_certo = false;
} else {
resultado = a / b;
}
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
deu_certo = false;
}
if (deu_certo) {
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
std::cout << "Até logo!\n";
}
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
char resposta = 'n';
do {
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
bool deu_certo = true;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
deu_certo = false;
} else {
resultado = a / b;
}
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
deu_certo = false;
}
if (deu_certo) {
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
} while (resposta == 's' || resposta == 'S');
std::cout << "Até logo!\n";
}
Etapa 5 · Validar o que foi digitado
A calculadora já se defende de operações erradas e da divisão por zero. Falta o pior caso: a pessoa digitar um texto onde se espera um número. Veja o que a versão da etapa 4 faz com abc:
$ ./calculadora === Calculadora da Ana === Primeiro número: abc Operação (+ - * /): Segundo número: Operação desconhecida: Outra conta? (s/n): Até logo!
A Ana digitou abc uma vez, e o programa disparou todas as perguntas sem esperar e terminou. O que aconteceu:
- O
std::cin >> atentou ler um número, encontrouabce falhou. Ele coloca0emae deixa oabcna entrada. - Mais importante: o
cinentra em estado de falha. Enquanto estiver assim, toda leitura seguinte falha na hora, sem esperar o teclado. Por isso oopficou com o espaço do começo (Operação desconhecida:) e arespostaficou'n'.
Para se defender, o programa precisa de três coisas (a aula Validar a Entrada explica cada uma com calma):
- while (!(std::cin >> a))
- O próprio
std::cin >> aresponde se deu certo: valetruese leu um número efalsese falhou. O!inverte: "enquanto a leitura falhar, repita". - std::cin.clear();
- Tira o
cindo estado de falha ("limpa" o erro), para ele voltar a ler. - std::getline(std::cin, lixo);
- Lê o resto da linha (o
abc) e guarda emlixo. Sem isso, oabccontinuaria lá, e a próxima tentativa falharia de novo. De quebra, podemos mostrar o que a pessoa digitou. - if (std::cin.eof()) return 0;
eof= end of file, "fim do arquivo": a entrada acabou (a pessoa apertou Ctrl+D, ou a entrada veio de um arquivo que terminou). Aí não adianta pedir de novo: encerramos.
O código da etapa 5, com a validação nos dois números:
#include <iostream>
#include <string>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
char resposta = 'n';
do {
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
while (!(std::cin >> a)) {
if (std::cin.eof()) {
return 0;
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << "Primeiro número: ";
}
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
while (!(std::cin >> b)) {
if (std::cin.eof()) {
return 0;
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << "Segundo número: ";
}
double resultado = 0.0;
bool deu_certo = true;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
deu_certo = false;
} else {
resultado = a / b;
}
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
deu_certo = false;
}
if (deu_certo) {
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
} while (resposta == 's' || resposta == 'S');
std::cout << "Até logo!\n";
}
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora $ ./calculadora === Calculadora da Ana === Primeiro número: abc "abc" não é um número. Tente de novo. Primeiro número: 10 Operação (+ - * /): + Segundo número: dez "dez" não é um número. Tente de novo. Segundo número: 10 10 + 10 = 20 Outra conta? (s/n): n Até logo!
Funciona, mas repare: o bloco de validação aparece duas vezes, quase igual, uma para o a e outra para o b. Se a Ana quiser mudar a mensagem, vai ter de mudar nos dois lugares (e um dia vai esquecer um). Código repetido é o sinal clássico de que falta uma função, e é isso que a etapa 6 resolve.
clear(): laço infinito
Sem o std::cin.clear();, o cin nunca sai do estado de falha. O getline também falha (não lê nada, por isso o "" vazio), a leitura do número falha de novo, e o laço gira para sempre. Para não travar o terminal, a Ana rodou com timeout 3 (para depois de 3 segundos) e head -6 (mostra só as 6 primeiras linhas):
$ printf "abc\n10\n" | timeout 3 ./calculadora | head -6 === Calculadora da Ana === Primeiro número: "" não é um número. Tente de novo. Primeiro número: "" não é um número. Tente de novo. Primeiro número: "" não é um número. Tente de novo. Primeiro número: "" não é um número. Tente de novo. Primeiro número: "" não é um número. Tente de novo.
Como consertar: a ordem é sempre clear() primeiro, depois descartar a linha. Se o seu programa travar assim no terminal, aperte Ctrl+C.
Missão (etapa 5): esta é a versão da etapa 4. A entrada da missão começa com abc. Rode e veja o programa disparar as perguntas. Depois troque o std::cin >> a; por um laço de validação que mostre "abc" não é um número. Tente de novo. e pergunte de novo. Lembre do clear() e do getline (e do #include <string>).
#include <iostream>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
char resposta = 'n';
do {
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
std::cin >> a;
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
std::cin >> b;
double resultado = 0.0;
bool deu_certo = true;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
deu_certo = false;
} else {
resultado = a / b;
}
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
deu_certo = false;
}
if (deu_certo) {
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
} while (resposta == 's' || resposta == 'S');
std::cout << "Até logo!\n";
}
#include <iostream>
#include <string>
int main() {
std::cout << "=== Calculadora da Ana ===\n";
char resposta = 'n';
do {
double a = 0.0;
double b = 0.0;
char op = ' ';
std::cout << "Primeiro número: ";
while (!(std::cin >> a)) {
if (std::cin.eof()) {
return 0;
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << "Primeiro número: ";
}
std::cout << "Operação (+ - * /): ";
std::cin >> op;
std::cout << "Segundo número: ";
while (!(std::cin >> b)) {
if (std::cin.eof()) {
return 0;
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << "Segundo número: ";
}
double resultado = 0.0;
bool deu_certo = true;
switch (op) {
case '+':
resultado = a + b;
break;
case '-':
resultado = a - b;
break;
case '*':
resultado = a * b;
break;
case '/':
if (b == 0) {
std::cout << "Não dá para dividir por zero.\n";
deu_certo = false;
} else {
resultado = a / b;
}
break;
default:
std::cout << "Operação desconhecida: " << op << '\n';
deu_certo = false;
}
if (deu_certo) {
std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
} while (resposta == 's' || resposta == 'S');
std::cout << "Até logo!\n";
}
Etapa 6 · Organizar em funções
O main ficou comprido e tem código repetido. Vamos dividi-lo em funções, cada uma com uma tarefa e um nome que diz qual é:
| Função | Tarefa | Recebe → devolve |
|---|---|---|
ler_numero | Mostrar uma pergunta e ler um número, repetindo até ser válido. | a pergunta (const std::string&) → o número (double) |
ler_operacao | Ler a operação, repetindo até ser + - * /. | nada → a operação (char) |
calcular | Fazer a conta. | dois números e a operação → o resultado (double) |
quer_continuar | Perguntar "Outra conta?". | nada → true ou false |
#include <cstdlib>
#include <iostream>
#include <string>
// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
double numero = 0.0;
std::cout << pergunta;
while (!(std::cin >> numero)) {
if (std::cin.eof()) {
std::exit(0); // a entrada acabou (Ctrl+D): encerra o programa
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << pergunta;
}
return numero;
}
// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
char op = ' ';
while (true) {
std::cout << "Operação (+ - * /): ";
if (!(std::cin >> op)) {
std::exit(0); // a entrada acabou
}
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
std::cout << "Operação desconhecida: " << op << '\n';
}
}
// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return 0.0;
}
bool quer_continuar() {
char resposta = 'n';
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
return resposta == 's' || resposta == 'S';
}
int main() {
std::cout << "=== Calculadora da Ana ===\n";
do {
double a = ler_numero("Primeiro número: ");
char op = ler_operacao();
double b = ler_numero("Segundo número: ");
if (op == '/' && b == 0) {
std::cout << "Não dá para dividir por zero.\n";
} else {
std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
}
} while (quer_continuar());
std::cout << "Até logo!\n";
}
Leia o main agora: ele cabe em poucas linhas e se lê quase como português. "Leia um número, leia a operação, leia outro número; se for divisão por zero, avise; senão, mostre a conta; repita enquanto a pessoa quiser continuar." Os detalhes de como cada coisa é feita estão escondidos nas funções.
- const std::string& pergunta
- A pergunta é passada por referência constante: a função usa o texto original, sem copiar e sem poder mudá-lo (aula Por Valor × Por Referência). Assim a mesma função serve para "Primeiro número: " e "Segundo número: ".
- std::exit(0);
- Encerra o programa inteiro, de dentro de qualquer função, com o código 0. Vem do
<cstdlib>. Usamos só quando a entrada acabou e não há mais o que fazer. - while (true) { … return op; }
- Em
ler_operacao, o laço "infinito" termina peloreturn, quando a operação é válida (veja break e continue). - case '+': return a + b;
- Em
calcular, cadacasedevolve o resultado direto: oreturnsai da função, então não precisa debreak. - return 0.0;
- Depois do
switch. Na prática nunca chega aqui (oler_operacaosó deixa passar as quatro operações), mas toda funçãodoubleprecisa devolver algo em todos os caminhos. Sem essa linha, o g++ avisa:control reaches end of non-void function. - } while (quer_continuar());
- A condição do laço é a própria chamada da função: ela pergunta, lê e devolve
trueoufalse.
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora $ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * /): + Segundo número: 5 10 + 5 = 15 Outra conta? (s/n): s Primeiro número: seis "seis" não é um número. Tente de novo. Primeiro número: 6 Operação (+ - * /): % Operação desconhecida: % Operação (+ - * /): * Segundo número: 1.5 6 * 1.5 = 9 Outra conta? (s/n): s Primeiro número: 1 Operação (+ - * /): / Segundo número: 0 Não dá para dividir por zero. Outra conta? (s/n): n Até logo!
Uma diferença de comportamento: agora uma operação errada é pedida de novo na hora (% foi recusado e a pergunta voltou), em vez de pedir o segundo número e só depois reclamar.
Missão (etapa 6): o main já chama ler_numero("Primeiro número: "), mas a função não existe. Rode e veja o erro (‘ler_numero’ was not declared in this scope no g++; use of undeclared identifier 'ler_numero' no Clang). Depois escreva a função double ler_numero(const std::string& pergunta) com a validação da etapa 5. A entrada começa com seis.
#include <cstdlib>
#include <iostream>
#include <string>
// Escreva aqui a função ler_numero(pergunta):
// mostra a pergunta, lê um double e repete se não for número.
// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
char op = ' ';
while (true) {
std::cout << "Operação (+ - * /): ";
if (!(std::cin >> op)) {
std::exit(0); // a entrada acabou
}
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
std::cout << "Operação desconhecida: " << op << '\n';
}
}
// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return 0.0;
}
bool quer_continuar() {
char resposta = 'n';
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
return resposta == 's' || resposta == 'S';
}
int main() {
std::cout << "=== Calculadora da Ana ===\n";
do {
double a = ler_numero("Primeiro número: ");
char op = ler_operacao();
double b = ler_numero("Segundo número: ");
if (op == '/' && b == 0) {
std::cout << "Não dá para dividir por zero.\n";
} else {
std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
}
} while (quer_continuar());
std::cout << "Até logo!\n";
}
#include <cstdlib>
#include <iostream>
#include <string>
// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
double numero = 0.0;
std::cout << pergunta;
while (!(std::cin >> numero)) {
if (std::cin.eof()) {
std::exit(0); // a entrada acabou (Ctrl+D): encerra o programa
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << pergunta;
}
return numero;
}
// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
char op = ' ';
while (true) {
std::cout << "Operação (+ - * /): ";
if (!(std::cin >> op)) {
std::exit(0); // a entrada acabou
}
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
std::cout << "Operação desconhecida: " << op << '\n';
}
}
// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return 0.0;
}
bool quer_continuar() {
char resposta = 'n';
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
return resposta == 's' || resposta == 'S';
}
int main() {
std::cout << "=== Calculadora da Ana ===\n";
do {
double a = ler_numero("Primeiro número: ");
char op = ler_operacao();
double b = ler_numero("Segundo número: ");
if (op == '/' && b == 0) {
std::cout << "Não dá para dividir por zero.\n";
} else {
std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
}
} while (quer_continuar());
std::cout << "Até logo!\n";
}
O código completo
Esta é a calculadora final, a mesma da etapa 6. Copie, compile com g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora e use à vontade:
#include <cstdlib>
#include <iostream>
#include <string>
// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
double numero = 0.0;
std::cout << pergunta;
while (!(std::cin >> numero)) {
if (std::cin.eof()) {
std::exit(0); // a entrada acabou (Ctrl+D): encerra o programa
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << pergunta;
}
return numero;
}
// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
char op = ' ';
while (true) {
std::cout << "Operação (+ - * /): ";
if (!(std::cin >> op)) {
std::exit(0); // a entrada acabou
}
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
std::cout << "Operação desconhecida: " << op << '\n';
}
}
// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return 0.0;
}
bool quer_continuar() {
char resposta = 'n';
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
return resposta == 's' || resposta == 'S';
}
int main() {
std::cout << "=== Calculadora da Ana ===\n";
do {
double a = ler_numero("Primeiro número: ");
char op = ler_operacao();
double b = ler_numero("Segundo número: ");
if (op == '/' && b == 0) {
std::cout << "Não dá para dividir por zero.\n";
} else {
std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
}
} while (quer_continuar());
std::cout << "Até logo!\n";
}
Repare no que o projeto juntou: cin, switch, if, do-while, while, validação, funções com parâmetros e retorno. Nenhum desses assuntos é difícil sozinho; o projeto mostra como eles se encaixam.
Desafios extras
- Resto da divisão. Acrescente a operação
%. Cuidado: o%do C++ só funciona com inteiros. Paradouble, usestd::fmod(a, b), do<cmath>(veja Matemática). E resto da divisão por zero também não existe. - Potência. Acrescente
^comstd::pow(a, b). - Histórico. Guarde cada conta feita num
std::vector<std::string>e mostre todas antes do "Até logo!" (veja std::vector). - Resultado bonito. Mostre o resultado com duas casas decimais, usando
std::formatoustd::printlncom{:.2f}(veja std::format e std::print). - Lixo depois do número. Digite
12abcno primeiro número e descubra o que acontece (dica: ocinlê o12e deixa oabc). Como você faria a calculadora recusar essa entrada?
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
calculadora.cpp: In function ‘double calcular(double, char, double)’:
calculadora.cpp:45:28: error: invalid operands of types ‘double’ and ‘double’ to binary ‘operator%’
45 | case '%': return a % b;
| ~ ^ ~
| | |
| | double
| double
O que significa: "operandos inválidos dos tipos double e double para o operador binário %". O desenho embaixo da linha mostra o tipo de cada lado. O % só aceita inteiros (veja Operadores Aritméticos).
Como consertar: return std::fmod(a, b); com #include <cmath>.
Desafio 1 no editor: acrescente a operação % com std::fmod. Mude três lugares: a condição de ler_operacao, o switch de calcular e o if da divisão por zero no main. Com a entrada da missão, a calculadora deve mostrar 10 % 3 = 1 e 7.5 % 2 = 1.5.
#include <cstdlib>
#include <iostream>
#include <string>
// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
double numero = 0.0;
std::cout << pergunta;
while (!(std::cin >> numero)) {
if (std::cin.eof()) {
std::exit(0); // a entrada acabou (Ctrl+D): encerra o programa
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << pergunta;
}
return numero;
}
// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
char op = ' ';
while (true) {
std::cout << "Operação (+ - * /): ";
if (!(std::cin >> op)) {
std::exit(0); // a entrada acabou
}
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
std::cout << "Operação desconhecida: " << op << '\n';
}
}
// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return 0.0;
}
bool quer_continuar() {
char resposta = 'n';
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
return resposta == 's' || resposta == 'S';
}
int main() {
std::cout << "=== Calculadora da Ana ===\n";
do {
double a = ler_numero("Primeiro número: ");
char op = ler_operacao();
double b = ler_numero("Segundo número: ");
if (op == '/' && b == 0) {
std::cout << "Não dá para dividir por zero.\n";
} else {
std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
}
} while (quer_continuar());
std::cout << "Até logo!\n";
}
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>
// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
double numero = 0.0;
std::cout << pergunta;
while (!(std::cin >> numero)) {
if (std::cin.eof()) {
std::exit(0); // a entrada acabou (Ctrl+D): encerra o programa
}
std::cin.clear();
std::string lixo;
std::getline(std::cin, lixo);
std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
std::cout << pergunta;
}
return numero;
}
// Lê a operação. Repete até ser + - * / ou %.
char ler_operacao() {
char op = ' ';
while (true) {
std::cout << "Operação (+ - * / %): ";
if (!(std::cin >> op)) {
std::exit(0); // a entrada acabou
}
if (op == '+' || op == '-' || op == '*' || op == '/' || op == '%') {
return op;
}
std::cout << "Operação desconhecida: " << op << '\n';
}
}
// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
case '%': return std::fmod(a, b);
}
return 0.0;
}
bool quer_continuar() {
char resposta = 'n';
std::cout << "Outra conta? (s/n): ";
std::cin >> resposta;
return resposta == 's' || resposta == 'S';
}
int main() {
std::cout << "=== Calculadora da Ana ===\n";
do {
double a = ler_numero("Primeiro número: ");
char op = ler_operacao();
double b = ler_numero("Segundo número: ");
if ((op == '/' || op == '%') && b == 0) {
std::cout << "Não dá para dividir por zero.\n";
} else {
std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
}
} while (quer_continuar());
std::cout << "Até logo!\n";
}
Na máquina da Ana, a solução roda assim:
$ ./calculadora === Calculadora da Ana === Primeiro número: 10 Operação (+ - * / %): % Segundo número: 3 10 % 3 = 1 Outra conta? (s/n): s Primeiro número: 7.5 Operação (+ - * / %): % Segundo número: 2 7.5 % 2 = 1.5 Outra conta? (s/n): n Até logo!
Exercícios
1. A Ana digitou 2,5 num std::cin >> a com a do tipo double. O que fica em a?
2. Num laço de validação, o que acontece se você esquecer o std::cin.clear()?
3. Por que a calculadora usa do-while e não while?
4. Quanto vale 10.0 / 0 com double, no g++?
5. Complete o laço que repete enquanto a leitura de a falhar:
6. Qual instrução tira o cin do estado de falha?
Resumo
std::cin >> xlê números com ponto decimal; ele separa os valores por espaços, não por linhas.- O
switchescolhe a conta; cadacaseprecisa do seubreak(o-Wextraavisa quando falta). - Divisão de
doublepor zero dáinfounan; confira o divisor antes. do { … } while (continuar);faz o "menu que repete"; umbool deu_certopula a conta sem fechar o programa.- Quando o
cinfalha: teste a leitura,clear(), descarte a linha comgetlinee pergunte de novo. Trate o fim da entrada (eof()). - Código repetido pede uma função. Funções pequenas com nomes claros deixam o
mainlegível.
Próximo projeto: Jogo de Adivinhação, com números aleatórios, contagem de tentativas e recorde.