Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Entrada e saída com iostream

O cabeçalho <iostream> traz os fluxos padrão do C++: std::cout para escrever na tela, std::cin para ler o que o usuário digita e std::cerr para mensagens de erro. A saída usa o operador <<, que insere o valor no fluxo; a entrada usa >>, que extrai um valor do fluxo e o converte para o tipo da variável de destino (int, double, std::string...).

C++ I/O Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
C++ / I/O

Entrada e saída com iostream

por Callahan Whitmore
Iniciante

Entenda o conceito

O cabeçalho <iostream> traz os fluxos padrão do C++: std::cout para escrever na tela, std::cin para ler o que o usuário digita e std::cerr para mensagens de erro. A saída usa o operador <<, que insere o valor no fluxo; a entrada usa >>, que extrai um valor do fluxo e o converte para o tipo da variável de destino (int, double, std::string...).

O >> pula espaços e quebras de linha antes do valor e para no primeiro espaço depois dele. Por isso, std::cin >> nome lê uma palavra só. Para ler uma linha inteira, com espaços, use std::getline(std::cin, texto). O cuidado está na mistura dos dois: o >> deixa no buffer o '\n' do Enter, e um getline logo depois leria uma linha vazia. Escrever std::getline(std::cin >> std::ws, texto) descarta esses espaços antes.

Quando o usuário digita algo que não combina com o tipo, como abc para um int, o fluxo entra em estado de falha e as leituras seguintes não acontecem. Teste com if (!std::cin) antes de usar os valores.

Para formatar a saída, <iomanip> oferece std::setw (largura do próximo valor), std::left e std::right (alinhamento), além de std::fixed e std::setprecision (casas decimais). Prefira '\n' para quebrar linha; std::endl também quebra, mas força o envio imediato (flush), o que é mais lento em saídas grandes.

↳ Pontos-chave
  • std::cout << valor escreve e std::cin >> variavel lê; os dois podem ser encadeados (std::cin >> a >> b;).
  • O >> lê até o próximo espaço ou quebra de linha; para uma linha com espaços, use std::getline.
  • Depois de um >>, leia a linha com std::getline(std::cin >> std::ws, texto) para descartar o '\n' que sobrou no buffer.
  • Se a conversão falhar (texto onde se esperava número), o fluxo fica em estado de falha: confira com if (!std::cin) e mande o erro para std::cerr.
  • std::setw vale só para o próximo valor, enquanto std::left, std::right, std::fixed e std::setprecision continuam valendo até serem trocados.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01Caixa da padaria com cin e cout

caixa.cpp
// Caixa da padaria: lê quantidade e preço e mostra o total
#include <iostream>

int main() {
    int quantidade = 0;  // valor inicial evita lixo se a leitura falhar
    double preco = 0.0;

    // Sem '\n' no fim, a resposta é digitada na mesma linha da pergunta
    std::cout << "Quantos pães? ";
    std::cin >> quantidade;  // >> converte o texto digitado em int

    std::cout << "Preço de cada um (use ponto): ";
    std::cin >> preco;  // aqui o texto vira double

    double total = quantidade * preco;

    // Vários << encadeados montam uma linha só
    std::cout << quantidade << " pães a R$ " << preco
              << " = R$ " << total << '\n';

    // >> também encadeia: lê dois valores separados por espaço ou Enter
    int mesa = 0;
    int pessoas = 0;
    std::cout << "Mesa e número de pessoas: ";
    std::cin >> mesa >> pessoas;
    std::cout << "Mesa " << mesa << " com " << pessoas << " pessoas\n";

    return 0;
}

O programa faz uma pergunta com std::cout e lê a resposta com std::cin >>, que converte o texto digitado para o tipo da variável: quantidade vira int e preco vira double. Na última leitura, dois >> encadeados pegam a mesa e o número de pessoas, separados por espaço ou por Enter.

Com as respostas 8, 0.75 e 4 3, o programa imprime 8 pães a R$ 0.75 = R$ 6 e Mesa 4 com 3 pessoas. Repare que o total sai como 6, e não 6.00: sem std::fixed e std::setprecision, o std::cout omite os zeros à direita de um double.

02Cadastro de livro com getline e std::ws

acervo.cpp
// Cadastro no acervo: getline para textos com espaços
#include <iostream>
#include <string>

int main() {
    std::string titulo;
    std::string autor;
    int ano = 0;

    // >> pararia no primeiro espaço e leria só "O" de "O Cortiço"
    std::cout << "Título: ";
    std::getline(std::cin, titulo);  // lê a linha inteira e descarta o '\n'

    std::cout << "Ano de publicação: ";
    std::cin >> ano;  // lê o número, mas deixa o '\n' do Enter no buffer

    // Sem std::ws, o getline acharia aquele '\n' e o autor ficaria vazio
    std::cout << "Autor: ";
    std::getline(std::cin >> std::ws, autor);  // ws pula espaços e quebras

    std::cout << "\nFicha do acervo\n";
    std::cout << "Título: " << titulo << '\n';
    std::cout << "Autor: " << autor << '\n';
    std::cout << "Ano: " << ano << '\n';

    // std::endl quebra a linha e força o envio imediato (flush)
    std::cout << "Cadastro concluído." << std::endl;

    return 0;
}

O título tem espaço, então é lido com std::getline, que guarda a linha inteira. Já o ano é lido com >>, que para depois do número e deixa o '\n' do Enter esperando no buffer. Se o autor fosse lido com std::getline(std::cin, autor), ele encontraria esse '\n' na hora e ficaria vazio, sem o usuário conseguir digitar nada.

std::cin >> std::ws resolve o problema: ele consome espaços e quebras de linha até o primeiro caractere visível, e só então o getline lê Aluísio Azevedo. A última linha usa std::endl, que quebra a linha e esvazia o buffer na hora; nas outras, '\n' basta.

03Ficha do torneio com validação e setw

torneio.cpp
// Ficha do torneio: valida a leitura e alinha a saída com setw
#include <iomanip>  // std::setw, std::left, std::fixed, std::setprecision
#include <iostream>
#include <string>

int main() {
    std::string jogador;
    int vitorias = 0;
    int derrotas = 0;

    std::cout << "Jogador, vitórias e derrotas: ";
    std::cin >> jogador >> vitorias >> derrotas;

    // Se algo não for número, o fluxo entra em estado de falha
    if (!std::cin) {
        std::cerr << "Entrada inválida: use números inteiros\n";
        return 1;  // código diferente de 0 indica erro
    }

    const int partidas = vitorias + derrotas;
    const double aproveitamento =
        partidas > 0 ? 100.0 * vitorias / partidas : 0.0;

    // setw vale só para o próximo valor; left e right ficam até serem trocados
    std::cout << '\n' << std::left << std::setw(10) << "Jogador"
              << std::right << std::setw(4) << "V" << std::setw(4) << "D"
              << std::setw(9) << "Aprov." << '\n';
    std::cout << std::left << std::setw(10) << jogador
              << std::right << std::setw(4) << vitorias << std::setw(4) << derrotas
              << std::fixed << std::setprecision(1)
              << std::setw(8) << aproveitamento << "%\n";

    return 0;
}

Se o usuário digitar Kira sete 3, o >> não consegue transformar sete em int, o fluxo entra em estado de falha e !std::cin fica verdadeiro. A mensagem vai para std::cerr, a saída própria para erros, e o return 1 encerra o programa avisando que algo deu errado. Com Kira 7 3, a leitura funciona e o aproveitamento é 70.0.

Na tabela, std::setw(n) reserva n colunas só para o valor seguinte, por isso aparece antes de cada um. std::left alinha o nome à esquerda e std::right volta os números para a direita; esses dois, assim como std::fixed e std::setprecision(1), continuam valendo até serem trocados. Como cabeçalho e linha usam as mesmas larguras, as colunas ficam alinhadas.


Agora é com você

Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.

01Pontos do jogador em três fasesFácil

Leia o apelido de um jogador (uma palavra) e os pontos que ele fez em três fases, todos num único std::cin encadeado, e mostre os pontos e o total. Para conferir, digite Lia 120 85 140; a saída esperada é só o que o programa imprime, sem mensagens pedindo os dados.

↳ Requisitos
  • Ler o apelido em std::string e os pontos em três variáveis int.
  • Fazer a leitura com um único std::cin e quatro operadores >> encadeados.
  • Não imprimir mensagens pedindo os dados.
  • Com a entrada Lia 120 85 140, reproduzir exatamente a saída esperada.
código inicial
#include <iostream>
#include <string>

int main() {
    // Declare o apelido e os pontos das três fases

    // Leia tudo com um único std::cin

    // Mostre os pontos e o total

    return 0;
}
saída esperada
Jogador: Lia
Pontos por fase: 120, 85 e 140
Total: 345
Ver dica

O >> não se importa se os valores vêm separados por espaço ou por Enter: cada um continua a leitura de onde o anterior parou.

Ver resposta
#include <iostream>
#include <string>  // necessário para std::string

int main() {
    std::string jogador;
    int fase1 = 0;
    int fase2 = 0;
    int fase3 = 0;

    // Um único cin encadeado lê as quatro informações, na ordem
    std::cin >> jogador >> fase1 >> fase2 >> fase3;

    int total = fase1 + fase2 + fase3;

    std::cout << "Jogador: " << jogador << '\n';
    std::cout << "Pontos por fase: " << fase1 << ", " << fase2 << " e " << fase3 << '\n';
    std::cout << "Total: " << total << '\n';

    return 0;
}

Entrada digitada: Lia 120 85 140.

Passo a passo

  1. As variáveis começam com valor (0 nos int), assim nada de lixo de memória aparece se a leitura falhar.
  2. std::cin >> jogador lê a primeira palavra (Lia) e para no espaço; por isso o apelido precisa ser uma palavra só.
  3. Os três >> seguintes pulam o espaço e convertem 120, 85 e 140 em int, porque esse é o tipo das variáveis.
  4. Encadear >> é o mesmo que escrever quatro leituras seguidas: cada uma continua de onde a anterior parou.
  5. A soma é feita entre inteiros e guardada em total.
  6. Cada std::cout junta textos e valores com << e termina com '\n'; não há mensagem pedindo os dados, então a saída tem só o resultado.

Por que funciona

O >> lê do fluxo de entrada até o próximo espaço, tabulação ou quebra de linha e converte o pedaço lido para o tipo da variável de destino. Como espaços e Enter servem igualmente de separador, o programa funciona tanto com tudo numa linha quanto com um valor por linha.

Erros comuns

  • Esquecer o #include <string>: o código pode até compilar em alguns compiladores, porque <iostream> às vezes inclui <string> por tabela, mas não há garantia. Inclua sempre.
  • Inverter a seta: std::cin << fase1; não compila. Na entrada a seta aponta para a variável (>>), indicando para onde o dado vai.
  • Digitar um nome com espaço: com Lia Souza 120 85 140, o jogador recebe Lia, o fase1 tenta ler Souza, a leitura falha e os pontos ficam zerados. Para textos com espaço, use std::getline.

Outra forma de resolver

Dá para ler cada valor num std::cin separado (std::cin >> jogador; std::cin >> fase1; ...). O resultado é o mesmo; o encadeamento só deixa o código mais curto quando os dados chegam juntos.

Saída esperada

Jogador: Lia
Pontos por fase: 120, 85 e 140
Total: 345

02Etiqueta de estoque com getlineMédio

Leia, nesta ordem e uma por linha, a quantidade de um produto em estoque, o nome dele (que pode ter espaços) e o preço unitário, e mostre a etiqueta com o valor total em estoque. Para conferir, digite 12, Caneca de cerâmica azul e 24.90, sem mensagens pedindo os dados.

↳ Requisitos
  • Ler a quantidade com >>, o nome com std::getline e o preço com >>.
  • Usar std::ws para que o nome não seja lido como uma linha vazia.
  • Mostrar os valores em reais com std::fixed e std::setprecision(2).
  • Com a entrada indicada, reproduzir exatamente a saída esperada.
código inicial
#include <iomanip>
#include <iostream>
#include <string>

int main() {
    int quantidade = 0;
    std::string produto;
    double preco = 0.0;

    // Leia a quantidade, o nome do produto e o preço

    // Calcule e mostre a etiqueta

    return 0;
}
saída esperada
Produto: Caneca de cerâmica azul
Quantidade: 12 unidades
Preço unitário: R$ 24.90
Valor em estoque: R$ 298.80
Ver dica

Pense no que sobra no buffer depois que o >> lê o número 12 e no que o getline faz quando a primeira coisa que encontra é uma quebra de linha.

Ver resposta
#include <iomanip>   // std::fixed e std::setprecision
#include <iostream>
#include <string>

int main() {
    int quantidade = 0;
    std::string produto;
    double preco = 0.0;

    std::cin >> quantidade;                      // lê 12 e deixa o '\n' no buffer
    std::getline(std::cin >> std::ws, produto);  // ws descarta o '\n' antes da linha
    std::cin >> preco;                           // >> pula sozinho a quebra de linha

    double valorEstoque = quantidade * preco;

    std::cout << "Produto: " << produto << '\n';
    std::cout << "Quantidade: " << quantidade << " unidades\n";
    std::cout << std::fixed << std::setprecision(2);  // duas casas daqui em diante
    std::cout << "Preço unitário: R$ " << preco << '\n';
    std::cout << "Valor em estoque: R$ " << valorEstoque << '\n';

    return 0;
}

Entrada digitada, uma informação por linha: 12, Caneca de cerâmica azul e 24.90.

Passo a passo

  1. std::cin >> quantidade; converte 12 em int, mas o Enter digitado depois continua no buffer como '\n'.
  2. std::cin >> std::ws consome esse '\n' (e qualquer espaço) antes do getline.
  3. std::getline(..., produto) lê a linha inteira, com os espaços, e descarta o '\n' do final.
  4. std::cin >> preco; lê 24.90; o >> já pula espaços e quebras sozinho, então não precisa de std::ws.
  5. quantidade * preco promove o int para double e dá 298.80.
  6. std::fixed com std::setprecision(2) vale para todos os double impressos depois; o int da quantidade, impresso antes, sai sem casas.

Por que funciona

O >> e o getline tratam a quebra de linha de forma diferente: o >> ignora espaços e quebras antes do valor, mas não consome a quebra que vem depois dele; o getline lê até o primeiro '\n' que encontrar, mesmo que ele esteja logo no início. O std::ws resolve o encontro dos dois, limpando o caminho antes da leitura da linha.

Erros comuns

  • getline sem std::ws depois de >>: std::getline(std::cin, produto); lê uma linha vazia. O produto sai em branco e o >> preco tenta ler Caneca, falha e deixa o preço em 0.
  • Usar >> para o nome: std::cin >> produto; guarda só Caneca, e o >> preco tenta ler de. Linhas com espaço pedem getline.
  • Vírgula no preço: digitar 24,90 faz o >> ler apenas 24 e parar na vírgula. O std::cin usa ponto como separador decimal.
  • std::fixed antes da quantidade: não estraga nada aqui, porque int não tem casas decimais, mas deixar a formatação perto dos valores que ela afeta facilita a leitura.

Outra forma de resolver

Em vez de std::ws, dá para descartar o resto da linha com std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); (de <limits>). Essa forma é preferível quando a linha do número pode ter lixo depois dele; para entradas bem formadas, o std::ws é mais curto.

Saída esperada

Produto: Caneca de cerâmica azul
Quantidade: 12 unidades
Preço unitário: R$ 24.90
Valor em estoque: R$ 298.80

03Cupom da padaria em colunasDesafio

Leia três itens de uma compra, um por linha no formato quantidade preço nome (o nome pode ter espaços), verifique se a leitura deu certo e imprima um cupom com colunas alinhadas, o total de unidades e o valor da compra. Para conferir, digite 2 4.50 Pão francês, 1 12.90 Queijo minas e 3 3.25 Iogurte natural.

↳ Requisitos
  • Ler a quantidade e o preço com >> e o nome com std::getline(std::cin >> std::ws, ...).
  • Se alguma leitura falhar, mostrar Entrada inválida em std::cerr e encerrar com return 1.
  • Alinhar quantidade, preço e total com std::setw (larguras 3, 8 e 8), com o nome do item na última coluna.
  • Mostrar os valores com duas casas (std::fixed e std::setprecision(2)) e, com a entrada indicada, reproduzir exatamente a saída esperada.
código inicial
#include <iomanip>
#include <iostream>
#include <string>

int main() {
    // Variáveis dos três itens: quantidade, preço e nome

    // Leia as três linhas

    // Verifique se a leitura deu certo

    // Imprima o cupom em colunas

    return 0;
}
saída esperada
Qtd   Unit.   Total  Item
  2    4.50    9.00  Pão francês
  1   12.90   12.90  Queijo minas
  3    3.25    9.75  Iogurte natural
Unidades: 6
Total da compra: R$ 31.65
Ver dica

Depois de ler os dois números da linha, ainda sobra um espaço antes do nome; e o fluxo guarda uma falha até o fim, então um único teste depois de todas as leituras basta.

Ver resposta
#include <iomanip>   // std::setw, std::fixed e std::setprecision
#include <iostream>
#include <string>

int main() {
    int qtd1 = 0;
    int qtd2 = 0;
    int qtd3 = 0;
    double preco1 = 0.0;
    double preco2 = 0.0;
    double preco3 = 0.0;
    std::string item1;
    std::string item2;
    std::string item3;

    // Cada linha: quantidade, preço e o nome até o fim da linha
    std::cin >> qtd1 >> preco1;
    std::getline(std::cin >> std::ws, item1);
    std::cin >> qtd2 >> preco2;
    std::getline(std::cin >> std::ws, item2);
    std::cin >> qtd3 >> preco3;
    std::getline(std::cin >> std::ws, item3);

    // Uma falha em qualquer leitura fica registrada no fluxo
    if (!std::cin) {
        std::cerr << "Entrada inválida\n";
        return 1;
    }

    const double total1 = qtd1 * preco1;
    const double total2 = qtd2 * preco2;
    const double total3 = qtd3 * preco3;

    std::cout << std::fixed << std::setprecision(2);
    // Cabeçalho com as mesmas larguras das linhas; o nome fica por último
    std::cout << std::setw(3) << "Qtd" << std::setw(8) << "Unit." << std::setw(8) << "Total" << "  Item\n";
    std::cout << std::setw(3) << qtd1 << std::setw(8) << preco1 << std::setw(8) << total1 << "  " << item1 << '\n';
    std::cout << std::setw(3) << qtd2 << std::setw(8) << preco2 << std::setw(8) << total2 << "  " << item2 << '\n';
    std::cout << std::setw(3) << qtd3 << std::setw(8) << preco3 << std::setw(8) << total3 << "  " << item3 << '\n';
    std::cout << "Unidades: " << qtd1 + qtd2 + qtd3 << '\n';
    std::cout << "Total da compra: R$ " << total1 + total2 + total3 << '\n';

    return 0;
}

Entrada digitada, uma linha por item: 2 4.50 Pão francês, 1 12.90 Queijo minas e 3 3.25 Iogurte natural.

Passo a passo

  1. std::cin >> qtd1 >> preco1; lê os dois números do começo da linha e para logo antes do espaço que separa o preço do nome.
  2. std::getline(std::cin >> std::ws, item1); pula esse espaço e lê o resto da linha, com os espaços do nome (Pão francês), consumindo o '\n'.
  3. As outras duas linhas repetem o mesmo par de leituras; o >> da linha seguinte já ignora qualquer quebra que tenha sobrado.
  4. if (!std::cin) testa o fluxo uma vez só, depois de todas as leituras: quando uma falha, o fluxo fica em estado de falha e as leituras seguintes nem acontecem, então basta conferir no fim.
  5. A mensagem de erro vai para std::cerr, a saída de erros, e o return 1 avisa a quem rodou o programa que algo deu errado.
  6. std::setw(n) reserva n colunas e alinha o próximo valor à direita; ele precisa ser repetido antes de cada valor, porque vale para um só.
  7. O cabeçalho usa as mesmas larguras (3, 8 e 8) que as linhas, e por isso as colunas ficam alinhadas.
  8. Com std::fixed e std::setprecision(2), preços e totais saem com centavos; as quantidades e a soma das unidades, que são int, não mudam.

Por que funciona

A entrada mistura números e um texto com espaços na mesma linha. O >> resolve os números porque para no primeiro espaço, e o getline com std::ws pega o que sobrou da linha como um texto só. Na saída, std::setw transforma valores de tamanhos diferentes (4.50 e 12.90) em colunas de largura fixa.

O nome do item fica na última coluna de propósito: o std::setw conta bytes, e letras acentuadas como ã e ê ocupam 2 bytes em UTF-8. Numa coluna do meio, Pão francês desalinharia a tabela; no fim da linha, não há nada depois dele para desalinhar.

Erros comuns

  • Ler o nome com >>: std::cin >> qtd1 >> preco1 >> item1; guarda só Pão; a leitura seguinte tenta converter francês em int, falha e o programa mostra Entrada inválida.
  • Um setw para a linha toda: std::cout << std::setw(8) << preco1 << total1; alinha só o preço; o total sai colado. Repita std::setw antes de cada valor.
  • Testar o fluxo antes de ler tudo: conferir !std::cin só depois da primeira linha deixa as outras sem verificação. Teste depois da última leitura (ou após cada uma, se quiser mensagens específicas).
  • Mensagem de erro no std::cout: funciona na tela, mas mistura o erro com o resultado quando a saída é redirecionada para um arquivo. Erros vão para std::cerr.

Outra forma de resolver

Com C++20, std::format (de <format>) monta cada linha com larguras e casas na própria string: std::cout << std::format("{:3}{:8.2f}{:8.2f} {}\n", qtd1, preco1, total1, item1);. Ela é preferível quando o compilador já suporta C++20 e há muitas colunas; os manipuladores de <iomanip> funcionam em qualquer versão.

Saída esperada

Qtd   Unit.   Total  Item
  2    4.50    9.00  Pão francês
  1   12.90   12.90  Queijo minas
  3    3.25    9.75  Iogurte natural
Unidades: 6
Total da compra: R$ 31.65
← Anterior na trilha de C++Operadores

Últimas lições

Ver o arquivo completo →