📚 Capítulo do curso: 03 — Entrada e Saída Formatada e Buffers da Especialização em Sistemas com C. Leia o capítulo antes de começar.
Um check-in de terminal que lê nome, idade, altura e cidade do visitante e resolve, na prática, o bug clássico do buffer de entrada “sujo”, que faz um fgets parecer pular sozinho.
🔒 Regra de Ouro: até o Capítulo 03 você tem sequência, variáveis e tipos e, agora, E/S formatada com
scanf,fgets,getchareputchar. Ainda semif,switch, laços ou funções extras. Por isso o check-in não consegue validar uma idade negativa ou um nome vazio. A validação chega no Miniprojeto 05, e repetir o check-in para vários visitantes, no Miniprojeto 06.
| ⏱️ Tempo estimado | 📦 Você vai entregar | ✅ Pronto quando |
|---|---|---|
| 50 min | main.c + entrada.txt |
nenhum campo sai vazio no resumo, digitando ou com a entrada redirecionada |
fgets (texto com espaços) e scanf (números).fgets guarda o \n dentro da string, e removê-lo com segurança.\n que o scanf deixa no stdin e que faz o fgets seguinte ler uma linha vazia.getchar() e putchar() para manipular a entrada e a saída caractere a caractere.Você está construindo o terminal de check-in da recepção de um evento técnico. Em sequência, ele lê o nome completo (fgets), a idade (scanf("%d")), a altura (scanf("%f")) e a cidade natal (fgets), e imprime um resumo.
| ID | Requisito |
|---|---|
| RF01 | Compila sem nenhum aviso com gcc -Wall -Wextra -Wpedantic -std=c17. |
| RF02 | Inclui <stdio.h>, <stdlib.h> e <string.h>, usa int main(void) e termina com return EXIT_SUCCESS;. |
| RF03 | Nenhum if, switch, while, for ou função além de main. |
| RF04 | Lê o nome com fgets e remove o \n da string (ex.: com strcspn). |
| RF05 | Lê a idade com scanf("%d", ...) e a altura com scanf("%f", ...). |
| RF06 | Usa getchar() para descartar o \n deixado pelo último scanf, logo antes do fgets seguinte. |
| RF07 | Lê a cidade com fgets (removendo o \n) depois do getchar(), provando que a leitura não foi pulada. |
| RF08 | Usa putchar() pelo menos uma vez. |
| RF09 | O resumo final mostra os 4 campos com os valores corretos. |
graph LR
A["main() inicia"] --> B["fgets: nome\n(remove newline com strcspn)"]
B --> C["scanf: idade (%d)"]
C --> D["scanf: altura (%f)"]
D --> E["getchar()\nconsome newline residual"]
E --> F["fgets: cidade\n(remove newline com strcspn)"]
F --> G["Resumo formatado\n(printf + putchar)"]
G --> H["return EXIT_SUCCESS"]
style D fill:#b45309,stroke:#f59e0b,color:#ffffff
style E fill:#dc2626,stroke:#f87171,color:#ffffff
style G fill:#059669,stroke:#34d399,color:#ffffff
O mesmo do Miniprojeto 01: gcc 8 ou mais novo. No Windows, rode chcp 65001 se os emojis aparecerem quebrados.
🎯 Objetivo: ver o resumo com os valores iniciais, antes de ler qualquer coisa do teclado.
⌨️ Faça: crie a pasta c_mini_03, salve o código abaixo como main.c e rode:
gcc -Wall -Wextra -Wpedantic -std=c17 main.c -o app
./app # Linux/macOS
.\app.exe # Windows (PowerShell)
📄 src/esqueleto.c
/*
* Miniprojeto 03: Sistema de Terminal Interativo e Sanitizacao de Buffers
* Regra: sequencia + E/S formatada + buffer residual + getchar/putchar.
* Ainda SEM if, switch, lacos ou funcoes extras.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAM_MAX 80
int main(void) {
// Valores iniciais: aparecem no resumo enquanto as leituras (TODOs) nao forem feitas
char nome[TAM_MAX] = "(vazio)";
char cidade[TAM_MAX] = "(vazio)";
int idade = 0;
float altura_m = 0.0f;
printf("====================================================\n");
printf(" TERMINAL DE CHECK-IN - DIGITE SEUS DADOS\n");
printf("====================================================\n");
printf("Nome completo: ");
// TODO 1: leia o nome com fgets(nome, TAM_MAX, stdin)
// TODO 2: remova o '\n' residual com nome[strcspn(nome, "\n")] = '\0';
printf("Idade (anos): ");
// TODO 3: leia a idade com scanf("%d", &idade)
printf("Altura (m): ");
// TODO 4: leia a altura com scanf("%f", &altura_m)
// TODO 5: chame getchar() aqui para descartar o '\n' que o scanf
// da altura deixou parado no buffer (senao o proximo fgets "pula")
printf("Cidade natal: ");
// TODO 6: leia a cidade com fgets e remova o '\n' residual (igual ao nome)
putchar('\n');
printf("====================================================\n");
printf(" RESUMO DO CHECK-IN\n");
printf("====================================================\n");
printf("Nome : %s\n", nome);
printf("Idade : %d anos\n", idade);
printf("Altura : %.2f m\n", altura_m);
printf("Cidade : %s\n", cidade);
printf("====================================================\n");
return EXIT_SUCCESS;
}
✅ Checkpoint: zero avisos, e o programa termina sozinho, sem esperar digitação:
🖥️ Saída esperada:
====================================================
TERMINAL DE CHECK-IN - DIGITE SEUS DADOS
====================================================
Nome completo: Idade (anos): Altura (m): Cidade natal:
====================================================
RESUMO DO CHECK-IN
====================================================
Nome : (vazio)
Idade : 0 anos
Altura : 0.00 m
Cidade : (vazio)
====================================================
Repare que as perguntas saem todas na mesma linha: ainda não há leitura nenhuma entre elas.
fgets (TODO 1 e 2)🎯 Objetivo: ler texto com espaços e entender o \n que vem junto.
⌨️ Faça:
fgets(nome, TAM_MAX, stdin);.Ana Maria e observe o resumo: a linha seguinte a Nome : Ana Maria fica em branco. É o \n do Enter, guardado dentro da string.nome[strcspn(nome, "\n")] = '\0'; e rode de novo.✅ Checkpoint: Nome : Ana Maria e, na linha logo abaixo, Idade : 0 anos, sem linha em branco no meio.
🎯 Objetivo: reproduzir o bug do buffer residual antes de corrigi-lo.
⌨️ Faça: leia a idade com scanf("%d", &idade);, a altura com scanf("%f", &altura_m); e a cidade com fgets + strcspn, igual ao nome. Ainda não faça o TODO 5. Rode e digite os dados.
✅ Checkpoint: depois de digitar a altura e apertar Enter, o programa não espera você digitar a cidade e o resumo mostra Cidade : vazio. O bug está reproduzido.
💡 O
scanf("%f")leu1.68e parou no\n, que ficou esperando no buffer. Ofgetsda cidade encontrou esse\nsozinho e o tratou como uma linha vazia.
🧯 Se der errado: se o gcc avisar ignoring return value of 'scanf', o seu compilador está com _FORTIFY_SOURCE ligado. Pode seguir: conferir o retorno do scanf exige if, que só chega no Capítulo 05.
getchar() (TODO 5)🎯 Objetivo: consumir exatamente o \n pendente.
⌨️ Faça: no lugar do TODO 5, logo antes do printf("Cidade natal: "), chame getchar();. Rode e digite os dados de novo.
✅ Checkpoint: o programa espera a cidade, e o resumo mostra os 4 campos preenchidos.
🎯 Objetivo: testar sem digitar tudo de novo, como fazem os testes automatizados.
⌨️ Faça: na pasta do projeto, crie entrada.txt com uma resposta por linha:
📄 tests/main.entrada.txt
Ana Maria Souza
21
1.68
Porto Alegre
./app < entrada.txt # Linux/macOS
Get-Content entrada.txt | .\app.exe # Windows (PowerShell)
✅ Checkpoint: com a entrada redirecionada, o teclado não “ecoa” o que foi digitado, então as perguntas saem coladas na mesma linha. O resumo, que é o que importa, tem que bater:
🖥️ Saída esperada:
====================================================
⚡ TERMINAL DE CHECK-IN - DIGITE SEUS DADOS
====================================================
Nome completo: Idade (anos): Altura (m): Cidade natal:
====================================================
📋 RESUMO DO CHECK-IN
====================================================
Nome : Ana Maria Souza
Idade : 21 anos
Altura : 1.68 m
Cidade : Porto Alegre
====================================================
Confira também o código de retorno (echo $? ou echo $LASTEXITCODE → 0).
🧯 Se der errado: se a cidade sair vazia só no teste redirecionado, confira se o entrada.txt termina com uma quebra de linha depois de Porto Alegre.
src/main.c📄 src/main.c
/*
* Miniprojeto 03: Sistema de Terminal Interativo e Sanitizacao de Buffers
* Especializacao em Engenharia de Sistemas em C
* Regra: sequencia + E/S formatada + buffer residual + getchar/putchar.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAM_MAX 80
int main(void) {
char nome[TAM_MAX];
char cidade[TAM_MAX];
int idade;
float altura_m;
printf("====================================================\n");
printf(" ⚡ TERMINAL DE CHECK-IN - DIGITE SEUS DADOS\n");
printf("====================================================\n");
printf("Nome completo: ");
fgets(nome, TAM_MAX, stdin);
// fgets guarda o '\n' digitado dentro da propria string; strcspn acha
// a posicao do primeiro '\n' e a substituimos por '\0' (fim de string).
nome[strcspn(nome, "\n")] = '\0';
printf("Idade (anos): ");
scanf("%d", &idade);
printf("Altura (m): ");
scanf("%f", &altura_m);
// O ultimo scanf("%f", ...) LEU o numero mas deixou o '\n' (Enter)
// parado no buffer de entrada. Se chamassemos fgets agora sem limpar
// esse '\n' residual, o fgets leria uma linha vazia e "pularia" a
// pergunta seguinte. getchar() consome exatamente esse '\n' pendente.
getchar();
printf("Cidade natal: ");
fgets(cidade, TAM_MAX, stdin);
cidade[strcspn(cidade, "\n")] = '\0';
putchar('\n');
printf("====================================================\n");
printf(" 📋 RESUMO DO CHECK-IN\n");
printf("====================================================\n");
printf("Nome : %s\n", nome);
printf("Idade : %d anos\n", idade);
printf("Altura : %.2f m\n", altura_m);
printf("Cidade : %s\n", cidade);
printf("====================================================\n");
return EXIT_SUCCESS;
}
Por que isso importa: scanf("%d") e scanf("%f") param no primeiro caractere que não é número, e o \n do Enter fica no buffer. Um scanf numérico seguinte não se importa, porque pula espaços em branco antes de converter. Já o fgets lê a linha inteira, inclusive esse \n solitário. Esse descompasso entre “quem ignora espaço em branco” e “quem não ignora” é o bug mais reportado por iniciantes em C.
Leia mais um campo com fgets (por exemplo, o e-mail de contato) entre a idade e a altura.
✅ Checkpoint: o e-mail aparece no resumo e nenhum campo sai vazio. Dica: depois do scanf da idade vem um fgets, então você precisa de mais um getchar().
Para atender o próximo visitante, ainda é preciso executar o programa de novo. Repetir o check-in na mesma execução exige um laço, assunto do Miniprojeto 06.
📋 Projetos do curso de C | 📚 Teoria do Capítulo 03 | 🗂️ Hub de projetos