Sistema de Cadastro de Alunos — struct · lista encadeada · arquivo binário · menu interativo
Esta aula une tudo que foi estudado nas 39 aulas anteriores em um único programa funcional: um sistema de cadastro de alunos para console que roda no Windows (ou em qualquer terminal).
| Opção | Operação | Conceitos usados |
|---|---|---|
| 1 | Inserir aluno | malloc, lista encadeada, struct |
| 2 | Listar todos | percurso da lista, printf formatado |
| 3 | Buscar por matrícula | busca sequencial, ponteiros |
| 4 | Remover aluno | remoção em lista encadeada, free |
| 5 | Salvar em arquivo | fwrite, arquivo binário |
| 6 | Carregar de arquivo | fread, fseek, ftell |
| 0 | Salvar e sair | liberação de memória, fclose |
O programa define dois tipos: a struct que representa um aluno e o nó da lista encadeada.
#include <stdio.h> #include <stdlib.h> #include <string.h> /* ─── Dados de um aluno ──────────────────────────────────────── */ typedef struct { int matricula; // chave de busca e ordenação char nome[60]; // nome com espaços — funciona em binário float nota; // 0.0 a 10.0 } Aluno; /* ─── Nó da lista encadeada ─────────────────────────────────── */ typedef struct No { Aluno aluno; // os dados struct No *prox; // ponteiro para o próximo nó } No; /* ─── Cabeça da lista ───────────────────────────────────────── */ No *lista = NULL; // começa vazia /* ─── Nome do arquivo de persistência ──────────────────────── */ const char *ARQUIVO = "cadastro.bin";
| Elemento | Por que assim |
|---|---|
nome[60] | Tamanho fixo dentro da struct → fwrite/fread gravam o nome completo, com espaços, sem complicação |
No *lista = NULL | Lista começa vazia — NULL é o sentinela de fim de lista |
const char *ARQUIVO | Nome do arquivo em uma constante — fácil de mudar em um só lugar |
Novos alunos são inseridos em ordem crescente de matrícula — a lista sempre fica ordenada, o que facilita a exibição e a busca.
/* Insere um aluno mantendo a lista ordenada por matrícula. Retorna 1 em sucesso, 0 se a matrícula já existe. */ int inserir(Aluno a) { /* 1. Verifica duplicata */ No *atual = lista; while (atual != NULL) { if (atual->aluno.matricula == a.matricula) { printf("Matrícula %d já cadastrada.\n", a.matricula); return 0; } atual = atual->prox; } /* 2. Aloca novo nó */ No *novo = (No *)malloc(sizeof(No)); if (novo == NULL) { printf("Memória insuficiente.\n"); return 0; } novo->aluno = a; novo->prox = NULL; /* 3. Inserção na posição correta (ordem crescente de matrícula) */ if (lista == NULL || a.matricula < lista->aluno.matricula) { // inserir na cabeça novo->prox = lista; lista = novo; } else { // encontra o nó antes da posição de inserção No *ant = lista; while (ant->prox != NULL && ant->prox->aluno.matricula < a.matricula) { ant = ant->prox; } novo->prox = ant->prox; ant->prox = novo; } return 1; }
/* Exibe todos os alunos com cabeçalho formatado */ void listar() { if (lista == NULL) { printf("Nenhum aluno cadastrado.\n"); return; } printf("\n%-8s | %-25s | %-5s | %s\n", "Matr.", "Nome", "Nota", "Situação"); printf("%-8s-+-%-25s-+-%-5s-+-%s\n", "--------", "-------------------------", "-----", "---------"); No *atual = lista; while (atual != NULL) { Aluno *a = &atual->aluno; printf("%-8d | %-25s | %-5.1f | %s\n", a->matricula, a->nome, a->nota, a->nota >= 5.0f ? "Aprovado" : "Reprovado"); atual = atual->prox; } printf("\n"); } /* Retorna ponteiro para o nó com a matrícula buscada, ou NULL */ No *buscarPorMatricula(int matricula) { No *atual = lista; while (atual != NULL) { if (atual->aluno.matricula == matricula) return atual; atual = atual->prox; } return NULL; }
buscarPorMatricula retorna No * em vez de Aluno. Isso permite tanto ler quanto modificar os dados do nó encontrado — e é também necessário para a remoção, que precisa do nó anterior.
A remoção em lista encadeada exige encontrar o nó anterior ao que será removido para reparar o elo:
/* Remove o aluno com a matrícula indicada. Retorna 1 se removido, 0 se não encontrado. */ int remover(int matricula) { if (lista == NULL) return 0; /* Caso especial: remover a cabeça */ if (lista->aluno.matricula == matricula) { No *remover = lista; lista = lista->prox; // avança a cabeça free(remover); return 1; } /* Caso geral: encontrar o nó anterior */ No *ant = lista; while (ant->prox != NULL && ant->prox->aluno.matricula != matricula) { ant = ant->prox; } if (ant->prox == NULL) return 0; // não encontrado No *alvo = ant->prox; ant->prox = alvo->prox; // repara o elo free(alvo); // libera a memória return 1; } /* Libera todos os nós da lista — chamada ao encerrar o programa */ void liberarLista() { No *atual = lista; while (atual != NULL) { No *prox = atual->prox; free(atual); atual = prox; } lista = NULL; }
Salvar percorre a lista e grava cada Aluno com fwrite. Carregar lê as structs de volta com fread e as reinseere na lista:
/* Salva todos os alunos da lista no arquivo binário. Retorna o número de alunos gravados. */ int salvar() { FILE *f = fopen(ARQUIVO, "wb"); if (f == NULL) { printf("Erro ao abrir '%s' para escrita.\n", ARQUIVO); return -1; } int count = 0; No *atual = lista; while (atual != NULL) { fwrite(&atual->aluno, sizeof(Aluno), 1, f); // grava a struct count++; atual = atual->prox; } fclose(f); return count; } /* Carrega alunos do arquivo binário para a lista. Retorna o número de alunos carregados, ou -1 se o arquivo não existe. */ int carregar() { FILE *f = fopen(ARQUIVO, "rb"); if (f == NULL) return -1; // arquivo ainda não existe — tudo bem /* descobre quantas structs tem no arquivo */ fseek(f, 0, SEEK_END); long bytes = ftell(f); rewind(f); int total = (int)(bytes / sizeof(Aluno)); int carregados = 0; Aluno a; while (fread(&a, sizeof(Aluno), 1, f) == 1) { if (inserir(a)) carregados++; } fclose(f); return carregados; }
inserir() em vez de reconstruir a lista diretamente? Porque inserir já trata duplicatas, mantém a ordem e aloca os nós corretamente. Reutilizar a função existente é mais seguro e elimina código duplicado.
main()/* Limpa o buffer de entrada após scanf */ void limparBuffer() { int c; while ((c = getchar()) != '\n' && c != EOF); } /* Exibe o menu e retorna a opção escolhida */ int menu() { int op; printf("\n╔══════════════════════════════════╗\n"); printf("║ SISTEMA DE CADASTRO — ALUNOS ║\n"); printf("╠══════════════════════════════════╣\n"); printf("║ 1. Inserir aluno ║\n"); printf("║ 2. Listar todos ║\n"); printf("║ 3. Buscar por matrícula ║\n"); printf("║ 4. Remover aluno ║\n"); printf("║ 5. Salvar em arquivo ║\n"); printf("║ 6. Recarregar do arquivo ║\n"); printf("║ 0. Salvar e sair ║\n"); printf("╚══════════════════════════════════╝\n"); printf("Opção: "); scanf("%d", &op); limparBuffer(); return op; } int main() { /* Carrega dados persistidos (se existirem) */ int carregados = carregar(); if (carregados > 0) printf("✔ %d aluno(s) carregado(s) de '%s'\n", carregados, ARQUIVO); else if (carregados == -1) printf("Nenhum arquivo anterior. Começando vazio.\n"); int op; do { op = menu(); if (op == 1) { /* ── Inserir ─────────────────────────────── */ Aluno a; printf("Matrícula: "); scanf("%d", &a.matricula); limparBuffer(); printf("Nome: "); fgets(a.nome, sizeof(a.nome), stdin); a.nome[strcspn(a.nome, "\n")] = '\0'; // remove \n printf("Nota: "); scanf("%f", &a.nota); limparBuffer(); if (inserir(a)) printf("✔ Aluno %d inserido.\n", a.matricula); } else if (op == 2) { /* ── Listar ──────────────────────────────── */ listar(); } else if (op == 3) { /* ── Buscar ──────────────────────────────── */ int mat; printf("Matrícula a buscar: "); scanf("%d", &mat); limparBuffer(); No *encontrado = buscarPorMatricula(mat); if (encontrado) { Aluno *a = &encontrado->aluno; printf("Encontrado: %d | %s | %.1f | %s\n", a->matricula, a->nome, a->nota, a->nota >= 5.0f ? "Aprovado" : "Reprovado"); } else { printf("Matrícula %d não encontrada.\n", mat); } } else if (op == 4) { /* ── Remover ─────────────────────────────── */ int mat; printf("Matrícula a remover: "); scanf("%d", &mat); limparBuffer(); if (remover(mat)) printf("✔ Aluno %d removido.\n", mat); else printf("Matrícula %d não encontrada.\n", mat); } else if (op == 5) { /* ── Salvar ──────────────────────────────── */ int n = salvar(); if (n >= 0) printf("✔ %d aluno(s) salvos em '%s'.\n", n, ARQUIVO); } else if (op == 6) { /* ── Recarregar ──────────────────────────── */ liberarLista(); lista = NULL; int n = carregar(); if (n >= 0) printf("✔ %d aluno(s) recarregado(s).\n", n); else printf("Arquivo '%s' não encontrado.\n", ARQUIVO); } else if (op != 0) { printf("Opção inválida.\n"); } } while (op != 0); /* Salva automaticamente e libera memória */ int n = salvar(); if (n >= 0) printf("✔ Dados salvos (%d aluno(s)). Até logo!\n", n); liberarLista(); return 0; }
Na próxima execução do programa, os dados são carregados automaticamente de cadastro.bin:
| Funcionalidade do projeto | Aulas que a sustentam |
|---|---|
Declaração de Aluno e No com typedef | Aulas 27, 29, 30 |
| Lista encadeada: inserir, remover, percorrer | Aulas 37, 38 |
Ponteiros No *, operador -> | Aulas 20, 28, 31 |
| malloc, free, verificação de NULL | Aulas 22, 38 |
| Menu com switch/do-while, scanf, fgets | Aulas 09, 10, 03, 26 |
| Funções com retorno e ponteiros | Aulas 13, 14 |
| fwrite, fread, fseek, ftell (arquivo binário) | Aula 39b |
Modo "wb" / "rb", fopen/fclose | Aula 39a |
| strcspn para remover \n de fgets | Aula 26 |
strstr; (2) permitir edição de nota sem remover e reinserir; (3) exportar a lista para um arquivo .csv texto; (4) ordenar por nota ao listar; (5) adicionar mais campos à struct (curso, data de nascimento).
Cole tudo em um único arquivo cadastro.c e compile com:
gcc cadastro.c -o cadastro .\cadastro.exe // Windows ./cadastro // Linux/macOS
/* ═══════════════════════════════════════════════════════════════
Sistema de Cadastro de Alunos — Projeto Integrador
AFF Education — Curso de C para Iniciantes
Conceitos: struct · lista encadeada · malloc/free · fread/fwrite
═══════════════════════════════════════════════════════════════ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int matricula;
char nome[60];
float nota;
} Aluno;
typedef struct No {
Aluno aluno;
struct No *prox;
} No;
No *lista = NULL;
const char *ARQUIVO = "cadastro.bin";
/* ── Inserção ordenada por matrícula ────────────────────────── */
int inserir(Aluno a) {
No *atual = lista;
while (atual != NULL) {
if (atual->aluno.matricula == a.matricula) {
printf("Matrícula %d já cadastrada.\n", a.matricula);
return 0;
}
atual = atual->prox;
}
No *novo = (No *)malloc(sizeof(No));
if (novo == NULL) { printf("Memória insuficiente.\n"); return 0; }
novo->aluno = a;
novo->prox = NULL;
if (lista == NULL || a.matricula < lista->aluno.matricula) {
novo->prox = lista;
lista = novo;
} else {
No *ant = lista;
while (ant->prox != NULL &&
ant->prox->aluno.matricula < a.matricula)
ant = ant->prox;
novo->prox = ant->prox;
ant->prox = novo;
}
return 1;
}
/* ── Listagem ───────────────────────────────────────────────── */
void listar() {
if (lista == NULL) { printf("Nenhum aluno cadastrado.\n"); return; }
printf("\n%-8s | %-25s | %-5s | %s\n",
"Matr.", "Nome", "Nota", "Situação");
printf("%-8s-+-%-25s-+-%-5s-+-%s\n",
"--------","-------------------------","-----","---------");
No *atual = lista;
while (atual != NULL) {
Aluno *a = &atual->aluno;
printf("%-8d | %-25s | %-5.1f | %s\n",
a->matricula, a->nome, a->nota,
a->nota >= 5.0f ? "Aprovado" : "Reprovado");
atual = atual->prox;
}
printf("\n");
}
/* ── Busca ──────────────────────────────────────────────────── */
No *buscarPorMatricula(int matricula) {
No *atual = lista;
while (atual != NULL) {
if (atual->aluno.matricula == matricula) return atual;
atual = atual->prox;
}
return NULL;
}
/* ── Remoção ────────────────────────────────────────────────── */
int remover(int matricula) {
if (lista == NULL) return 0;
if (lista->aluno.matricula == matricula) {
No *rem = lista; lista = lista->prox; free(rem); return 1;
}
No *ant = lista;
while (ant->prox != NULL &&
ant->prox->aluno.matricula != matricula)
ant = ant->prox;
if (ant->prox == NULL) return 0;
No *alvo = ant->prox;
ant->prox = alvo->prox;
free(alvo);
return 1;
}
/* ── Liberação total ────────────────────────────────────────── */
void liberarLista() {
No *atual = lista;
while (atual != NULL) {
No *prox = atual->prox; free(atual); atual = prox;
}
lista = NULL;
}
/* ── Salvar em arquivo binário ─────────────────────────────── */
int salvar() {
FILE *f = fopen(ARQUIVO, "wb");
if (f == NULL) {
printf("Erro ao salvar em '%s'.\n", ARQUIVO); return -1;
}
int count = 0;
No *atual = lista;
while (atual != NULL) {
fwrite(&atual->aluno, sizeof(Aluno), 1, f);
count++;
atual = atual->prox;
}
fclose(f);
return count;
}
/* ── Carregar do arquivo binário ──────────────────────────── */
int carregar() {
FILE *f = fopen(ARQUIVO, "rb");
if (f == NULL) return -1;
int carregados = 0;
Aluno a;
while (fread(&a, sizeof(Aluno), 1, f) == 1)
if (inserir(a)) carregados++;
fclose(f);
return carregados;
}
/* ── Utilitários ────────────────────────────────────────────── */
void limparBuffer() { int c; while ((c=getchar())!='\n'&&c!=EOF); }
int menu() {
int op;
printf("\n╔══════════════════════════════════╗\n");
printf("║ SISTEMA DE CADASTRO — ALUNOS ║\n");
printf("╠══════════════════════════════════╣\n");
printf("║ 1. Inserir aluno ║\n");
printf("║ 2. Listar todos ║\n");
printf("║ 3. Buscar por matrícula ║\n");
printf("║ 4. Remover aluno ║\n");
printf("║ 5. Salvar em arquivo ║\n");
printf("║ 6. Recarregar do arquivo ║\n");
printf("║ 0. Salvar e sair ║\n");
printf("╚══════════════════════════════════╝\n");
printf("Opção: ");
scanf("%d", &op);
limparBuffer();
return op;
}
/* ── Main ────────────────────────────────────────────────────── */
int main() {
int carregados = carregar();
if (carregados > 0)
printf("✔ %d aluno(s) carregado(s) de '%s'\n", carregados, ARQUIVO);
else
printf("Nenhum arquivo anterior. Começando vazio.\n");
int op;
do {
op = menu();
if (op == 1) {
Aluno a;
printf("Matrícula: "); scanf("%d", &a.matricula); limparBuffer();
printf("Nome: "); fgets(a.nome, sizeof(a.nome), stdin);
a.nome[strcspn(a.nome, "\n")] = '\0';
printf("Nota: "); scanf("%f", &a.nota); limparBuffer();
if (inserir(a)) printf("✔ Aluno %d inserido.\n", a.matricula);
} else if (op == 2) {
listar();
} else if (op == 3) {
int mat;
printf("Matrícula: "); scanf("%d", &mat); limparBuffer();
No *n = buscarPorMatricula(mat);
if (n) {
Aluno *a = &n->aluno;
printf("Encontrado: %d | %s | %.1f | %s\n",
a->matricula, a->nome, a->nota,
a->nota >= 5.0f ? "Aprovado" : "Reprovado");
} else printf("Matrícula %d não encontrada.\n", mat);
} else if (op == 4) {
int mat;
printf("Matrícula: "); scanf("%d", &mat); limparBuffer();
if (remover(mat)) printf("✔ Aluno %d removido.\n", mat);
else printf("Matrícula %d não encontrada.\n", mat);
} else if (op == 5) {
int n = salvar();
if (n >= 0) printf("✔ %d aluno(s) salvos.\n", n);
} else if (op == 6) {
liberarLista();
int n = carregar();
if (n >= 0) printf("✔ %d aluno(s) recarregado(s).\n", n);
else printf("Arquivo não encontrado.\n");
} else if (op != 0) {
printf("Opção inválida.\n");
}
} while (op != 0);
int n = salvar();
if (n >= 0) printf("✔ Dados salvos (%d aluno(s)). Até logo!\n", n);
liberarLista();
return 0;
}