Entrada e saída básica
Esta aula aborda as funções básicas de entrada e saída em C, como printf, scanf, putchar e getchar, além de explicar o funcionamento do buffer de entrada/saída. O aluno aprenderá a usar especificadores de formato, lidar com cuidados ao usar scanf e entender como o buffer afeta a leitura de dados.
Nesta aula, vamos explorar as funções fundamentais de entrada e saída (E/S) na linguagem C. Essas funções são essenciais para interagir com o usuário, exibindo informações e recebendo dados. Abordaremos printf, scanf, putchar e getchar, além do conceito de buffer, que influencia diretamente o comportamento da E/S. Compreender esses tópicos é crucial para escrever programas que se comuniquem corretamente com o ambiente.
printf e especificadores
A função printf é usada para exibir dados formatados na saída padrão (geralmente o terminal). Ela aceita uma string de formato que contém texto literal e especificadores de formato, que são substituídos pelos valores das variáveis fornecidas. Os especificadores começam com % e indicam o tipo de dado a ser impresso.
Os especificadores mais comuns incluem: %d para inteiros decimais, %f para números de ponto flutuante, %c para caracteres, %s para strings, %x para hexadecimais, entre outros. É possível modificar a formatação com flags, largura, precisão e modificadores de tamanho. Por exemplo, %10d imprime um inteiro com largura mínima de 10 caracteres, alinhado à direita; %.2f imprime um float com duas casas decimais.
#include <stdio.h>
int main() {
int idade = 25;
float altura = 1.75;
char inicial = 'J';
char nome[] = "João";
printf("Idade: %d anos\n", idade);
printf("Altura: %.2f m\n", altura);
printf("Inicial: %c\n", inicial);
printf("Nome: %s\n", nome);
printf("Hex: %x\n", idade);
return 0;
}
scanf e cuidados
scanf é a função padrão para leitura de dados formatados da entrada padrão (teclado). Ela também usa uma string de formato com especificadores, mas os argumentos devem ser endereços de variáveis (usando o operador &). Por exemplo, scanf("%d", &idade); lê um inteiro e armazena em idade.
É importante tomar cuidados com scanf: ela deixa o caractere de nova linha (\n) no buffer, o que pode causar problemas em leituras subsequentes, especialmente com %c. Além disso, scanf não é segura para strings, pois não verifica o tamanho do buffer, podendo causar estouro. Para strings, é recomendado usar fgets ou especificar largura máxima, como %19s para um array de 20 caracteres. Outro cuidado: a leitura de %c lê qualquer caractere, incluindo espaços e quebras de linha; muitas vezes é necessário limpar o buffer antes.
#include <stdio.h>
int main() {
int idade;
char nome[20];
char sexo;
printf("Digite sua idade: ");
scanf("%d", &idade);
printf("Digite seu nome: ");
scanf("%19s", nome); // lê até 19 caracteres + '\0'
// Limpar o buffer de entrada antes de ler caractere
while(getchar() != '\n'); // descarta o resto da linha
printf("Digite seu sexo (M/F): ");
scanf("%c", &sexo);
printf("\nIdade: %d, Nome: %s, Sexo: %c\n", idade, nome, sexo);
return 0;
}
putchar/getchar
putchar e getchar são funções mais simples para E/S de um único caractere. putchar(c) escreve o caractere c na saída padrão. getchar() lê um caractere da entrada padrão e o retorna como um int (para poder representar EOF). Elas são úteis para processamento caractere por caractere, como em menus ou leitura de arquivos.
Um exemplo clássico é um programa que copia a entrada para a saída até encontrar EOF (Ctrl+D no Linux, Ctrl+Z no Windows):
#include <stdio.h>
int main() {
int c;
printf("Digite caracteres (Ctrl+D para sair):\n");
while ((c = getchar()) != EOF) {
putchar(c);
}
return 0;
}
Note que getchar retorna int para distinguir caracteres válidos de EOF. A variável c deve ser int, não char.
Buffer
O buffer é uma área de memória temporária usada para armazenar dados antes de serem processados. Em C, a E/S padrão é geralmente bufferizada: os dados são acumulados em um buffer e transferidos em blocos para o dispositivo (terminal, arquivo). Isso melhora a eficiência, mas pode causar comportamentos inesperados, como quando scanf deixa caracteres no buffer.
Existem três tipos de bufferização: sem buffer (unbuffered), com buffer de linha (line-buffered) e com buffer completo (fully-buffered). Para a saída padrão (stdout), é comum ser line-buffered quando conectada a um terminal, ou seja, o buffer é liberado (flushed) quando uma nova linha é encontrada. A entrada padrão (stdin) também é line-buffered, o que explica por que getchar só retorna após pressionar Enter. Para forçar a liberação do buffer, pode-se usar fflush(stdout) ou setbuf.
#include <stdio.h>
int main() {
printf("Sem newline, sem flush? ");
// O texto pode não aparecer imediatamente
fflush(stdout); // força a saída
getchar(); // espera um caractere
return 0;
}
Entender o buffer é crucial para evitar problemas como leitura de lixo ou saída não exibida no momento esperado.
Boas práticas
- Sempre verifique o retorno de
scanfpara garantir que a leitura foi bem-sucedida. - Use
fgetspara ler strings com segurança, em vez descanfcom%s. - Limpe o buffer de entrada após usar
scanfse for ler caracteres ou strings comfgetsdepois. - Evite usar
fflush(stdin), pois é comportamento indefinido em C padrão; prefira loops comgetchar. - Para saída imediata sem newline, use
fflush(stdout).
Referências
- cppreference: printf
- cppreference: scanf
- cppreference: getchar
- cppreference: putchar
- cppreference: fflush
- GNU C Library: I/O Concepts
- Tutorialspoint: printf
Exercícios
- Escreva um programa que leia um número inteiro e um número de ponto flutuante, e os imprima com 3 casas decimais para o float e em formato hexadecimal para o inteiro.
- Explique por que o código abaixo pode falhar ao ler um caractere após um número e como corrigi-lo.
int num; char letra; scanf("%d", &num); scanf("%c", &letra); - Usando putchar e getchar, escreva um programa que leia uma linha de texto e a imprima em maiúsculas (assuma que o usuário digita apenas letras minúsculas).
- O que é o buffer de saída e como você pode forçar a saída imediata de uma mensagem sem usar uma quebra de linha?
- Escreva um programa que leia dois números inteiros e exiba a soma, diferença, produto e quociente (com duas casas decimais) usando printf.
#include <stdio.h>
int main() {
int inteiro;
float flutuante;
printf("Digite um inteiro e um float: ");
scanf("%d %f", &inteiro, &flutuante);
printf("Inteiro em hex: %x\n", inteiro);
printf("Float com 3 casas: %.3f\n", flutuante);
return 0;
}
scanf(" %c", &letra); ou limpar o buffer com while(getchar() != '\n'); antes do segundo scanf.
#include <stdio.h>
int main() {
int c;
printf("Digite uma linha (minúsculas): ");
while ((c = getchar()) != '\n' && c != EOF) {
if (c >= 'a' && c <= 'z')
putchar(c - 32); // diferença entre maiúscula e minúscula
else
putchar(c);
}
putchar('\n');
return 0;
}
fflush(stdout); após a chamada de printf.
#include <stdio.h>
int main() {
int a, b;
printf("Digite dois inteiros: ");
scanf("%d %d", &a, &b);
printf("Soma: %d\n", a + b);
printf("Diferença: %d\n", a - b);
printf("Produto: %d\n", a * b);
if (b != 0)
printf("Quociente: %.2f\n", (float)a / b);
else
printf("Divisão por zero\n");
return 0;
}