Entrada e saída com printf e scanf

1. Introdução às Funções de E/S Padrão

A linguagem C oferece funções poderosas para entrada e saída de dados através da biblioteca padrão <stdio.h>. Esta biblioteca fornece as funções printf (saída formatada) e scanf (entrada formatada), que são essenciais para a comunicação entre o programa e o usuário.

Os fluxos padrão em C são:
- stdin — fluxo de entrada padrão (geralmente o teclado)
- stdout — fluxo de saída padrão (geralmente o monitor)
- stderr — fluxo de saída de erro padrão

A principal diferença entre E/S formatada e não formatada está no controle sobre a apresentação dos dados. Enquanto funções como putchar e getchar trabalham caractere por caractere, printf e scanf permitem formatar e interpretar dados de maneira estruturada.

2. A Função printf: Saída Formatada

A sintaxe básica do printf é:

printf("string de formato", argumentos);

Os especificadores de formato mais comuns são:

Especificador Tipo de dado
%d inteiro (int)
%f ponto flutuante (float/double)
%c caractere (char)
%s string (char[])

Exemplo básico:

#include <stdio.h>

int main() {
    int idade = 25;
    float altura = 1.75;
    char inicial = 'A';
    char nome[] = "Maria";

    printf("Idade: %d anos\n", idade);
    printf("Altura: %.2f metros\n", altura);
    printf("Inicial: %c\n", inicial);
    printf("Nome: %s\n", nome);

    return 0;
}

Modificadores de largura e precisão:

printf("%10d\n", 42);       // largura mínima de 10 caracteres (alinhado à direita)
printf("%-10d\n", 42);      // alinhado à esquerda
printf("%.2f\n", 3.14159);  // duas casas decimais
printf("%10.3f\n", 3.14159);// largura 10, precisão 3

3. Especificadores Avançados em printf

Para inteiros, existem variações úteis:

int valor = 255;
printf("Decimal: %d\n", valor);       // 255
printf("Sem sinal: %u\n", valor);     // 255
printf("Hexadecimal: %x\n", valor);   // ff
printf("Octal: %o\n", valor);         // 377
printf("Longo: %ld\n", 123456L);      // para long int

Para ponto flutuante:

double pi = 3.1415926535;
printf("Notação científica: %e\n", pi);    // 3.141593e+00
printf("Formato compacto: %g\n", pi);      // 3.14159

Caracteres especiais:

printf("Nova linha: \n");
printf("Tabulação: \tTexto tabulado\n");
printf("Barra invertida: \\\n");
printf("Porcentagem: %%\n");

4. A Função scanf: Entrada Formatada

O scanf lê dados do teclado conforme uma string de formato. É crucial usar o operador & (endereço) para variáveis que não são arrays.

#include <stdio.h>

int main() {
    int numero;
    float real;
    char letra;

    printf("Digite um inteiro: ");
    scanf("%d", &numero);

    printf("Digite um float: ");
    scanf("%f", &real);

    printf("Digite um caractere: ");
    scanf(" %c", &letra);  // espaço antes de %c ignora whitespace

    printf("Você digitou: %d, %.2f, %c\n", numero, real, letra);

    return 0;
}

Captura de múltiplos valores:

int dia, mes, ano;
printf("Digite a data (dd/mm/aaaa): ");
scanf("%d/%d/%d", &dia, &mes, &ano);
printf("Data: %02d/%02d/%04d\n", dia, mes, ano);

5. Tratamento de Erros e Validação em scanf

O scanf retorna o número de itens lidos com sucesso. Isso é fundamental para validação:

#include <stdio.h>

int main() {
    int idade;
    int resultado;

    printf("Digite sua idade: ");
    resultado = scanf("%d", &idade);

    if (resultado == 1) {
        printf("Idade válida: %d\n", idade);
    } else {
        printf("Entrada inválida!\n");
    }

    return 0;
}

Problema do buffer residual: Quando o usuário digita algo que não corresponde ao formato esperado, os caracteres permanecem no buffer, causando loops infinitos em leituras subsequentes.

Solução: limpeza do buffer

void limpar_buffer() {
    int c;
    while ((c = getchar()) != '\n' && c != EOF);
}

Exemplo prático com validação:

#include <stdio.h>

int main() {
    int numero;
    int resultado;

    do {
        printf("Digite um número inteiro: ");
        resultado = scanf("%d", &numero);

        if (resultado != 1) {
            printf("Erro! Digite apenas números.\n");
            // Limpa o buffer
            int c;
            while ((c = getchar()) != '\n' && c != EOF);
        }
    } while (resultado != 1);

    printf("Número lido: %d\n", numero);
    return 0;
}

6. Leitura de Strings com scanf e Alternativas

O scanf com %s lê apenas até o primeiro espaço em branco:

char nome[50];
printf("Digite seu nome completo: ");
scanf("%s", nome);  // Lê apenas o primeiro nome!
printf("Nome: %s\n", nome);  // Exibe apenas "Maria" se digitou "Maria Silva"

Solução com fgets:

char nome_completo[100];
printf("Digite seu nome completo: ");
fgets(nome_completo, sizeof(nome_completo), stdin);
printf("Nome: %s", nome_completo);

Combinação fgets + sscanf para validação:

#include <stdio.h>
#include <string.h>

int main() {
    char entrada[100];
    int idade;
    float altura;

    printf("Digite idade e altura (ex: 25 1.75): ");
    fgets(entrada, sizeof(entrada), stdin);

    // Remove o \n do final
    entrada[strcspn(entrada, "\n")] = 0;

    if (sscanf(entrada, "%d %f", &idade, &altura) == 2) {
        printf("Idade: %d, Altura: %.2f\n", idade, altura);
    } else {
        printf("Formato inválido!\n");
    }

    return 0;
}

7. Exemplos Práticos e Boas Práticas

Calculadora simples:

#include <stdio.h>

int main() {
    float a, b, resultado;
    char operador;
    int items_lidos;

    printf("Digite: numero1 operador numero2 (ex: 10 + 5): ");
    items_lidos = scanf("%f %c %f", &a, &operador, &b);

    if (items_lidos != 3) {
        printf("Entrada inválida!\n");
        return 1;
    }

    switch (operador) {
        case '+': resultado = a + b; break;
        case '-': resultado = a - b; break;
        case '*': resultado = a * b; break;
        case '/':
            if (b == 0) {
                printf("Divisão por zero!\n");
                return 1;
            }
            resultado = a / b;
            break;
        default:
            printf("Operador inválido!\n");
            return 1;
    }

    printf("%.2f %c %.2f = %.2f\n", a, operador, b, resultado);
    return 0;
}

Leitura de data formatada:

#include <stdio.h>

int main() {
    int dia, mes, ano;
    char entrada[20];

    printf("Digite a data (DD/MM/AAAA): ");
    fgets(entrada, sizeof(entrada), stdin);

    if (sscanf(entrada, "%d/%d/%d", &dia, &mes, &ano) == 3) {
        if (dia >= 1 && dia <= 31 && mes >= 1 && mes <= 12 && ano >= 1900) {
            printf("Data válida: %02d/%02d/%04d\n", dia, mes, ano);
        } else {
            printf("Data inválida!\n");
        }
    } else {
        printf("Formato inválido!\n");
    }

    return 0;
}

Boas práticas resumidas:

  1. Sempre verifique o retorno de scanf
  2. Limpe o buffer após leituras com falha
  3. Prefira fgets + sscanf para entradas complexas
  4. Use tamanhos máximos em arrays de caracteres
  5. Evite scanf("%s", ...) sem limite de tamanho
  6. Utilize modificadores de formato para evitar estouro de buffer

Referências