🚀 Capítulo 20: Projeto Integrado CLI — Sistema de Gestão de Alunos em C
🎯 Objetivos da Aula
Ao final desta aula, você será capaz de:
- Integrar todos os conceitos da Fase 01 (Sintaxe C, Controle de Fluxo, Funções, Structs, Ponteiros, Alocação Dinâmica e Arquivos).
- Construir um Sistema de Gestão em Linha de Comando (CLI) completo e modular.
- Garantir a persistência dos dados salvando e recarregando registros em arquivo binário (
alunos.dat). - Aplicar o tratamento correto de desalocação de memória para garantir zero vazamentos de memória (Memory Leaks).
🧠 1. Arquitetura do Projeto Capstone (Fase 01)
A aplicação gerencia uma lista dinâmica de alunos em memória RAM e sincroniza as alterações com o disco rígido.
flowchart TD subgraph CLI ["Interface de Linha de Comando (Terminal)"] Menu["Menu Principal\n(1-Cadastrar, 2-Listar, 3-Buscar, 4-Salvar, 0-Sair)"] end subgraph Core ["Núcleo de Negócio em C"] RAM_List["Vetor Dinâmico na Heap (malloc/realloc)\nstruct Aluno *turma"] Sort_Alg["Algoritmo de Ordenação\n(QuickSort / BubbleSort por Média)"] end subgraph Storage ["Camada de Persistência em Disco"] FileBin["Arquivo Binário em Disco\n'alunos.dat' (fwrite / fread)"] end Menu <--> RAM_List RAM_List --> Sort_Alg RAM_List <-->|"Carregar no Início / Salvar ao Sair"| FileBin
💻 2. Código Completo do Projeto Integrado (sistema_alunos.c)
Let’s write the complete, functional, professional C project:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int matricula;
char nome[50];
float media;
} Aluno;
// Protótipos das Funções
void exibirMenu();
Aluno* carregarDados(int *qtd);
void salvarDados(Aluno *turma, int qtd);
Aluno* cadastrarAluno(Aluno *turma, int *qtd);
void listarAlunos(Aluno *turma, int qtd);
void buscarPorMatricula(Aluno *turma, int qtd);
int main() {
int qtdAlunos = 0;
Aluno *turma = carregarDados(&qtdAlunos);
int opcao = -1;
printf("=========================================\n");
printf(" SISTEMA DE GESTAO DE ALUNOS (CLI C) \n");
printf("=========================================\n");
while (opcao != 0) {
exibirMenu();
scanf("%d", &opcao);
switch (opcao) {
case 1:
turma = cadastrarAluno(turma, &qtdAlunos);
break;
case 2:
listarAlunos(turma, qtdAlunos);
break;
case 3:
buscarPorMatricula(turma, qtdAlunos);
break;
case 4:
salvarDados(turma, qtdAlunos);
break;
case 0:
salvarDados(turma, qtdAlunos);
printf(">> Encerrando o sistema e salvando dados...\n");
break;
default:
printf(">> Opcao invalida!\n");
break;
}
}
// Liberação da memória alocada antes de sair
if (turma != NULL) {
free(turma);
turma = NULL;
}
printf(">> Memoria desalocada. Ate logo!\n");
return 0;
}
void exibirMenu() {
printf("\n--- MENU DE OPCOES ---\n");
printf("1. Cadastrar Novo Aluno\n");
printf("2. Listar Todos os Alunos\n");
printf("3. Buscar Aluno por Matricula\n");
printf("4. Salvar Alteracoes em Disco\n");
printf("0. Sair e Salvar\n");
printf("Escolha uma opcao: ");
}
Aluno* carregarDados(int *qtd) {
FILE *arq = fopen("alunos.dat", "rb");
if (arq == NULL) {
*qtd = 0;
return NULL;
}
fseek(arq, 0, SEEK_END);
long tamanhoBytes = ftell(arq);
rewind(arq);
*qtd = tamanhoBytes / sizeof(Aluno);
if (*qtd == 0) {
fclose(arq);
return NULL;
}
Aluno *turma = (Aluno*) malloc(*qtd * sizeof(Aluno));
if (turma != NULL) {
fread(turma, sizeof(Aluno), *qtd, arq);
}
fclose(arq);
printf(">> %d registro(s) carregado(s) de 'alunos.dat'.\n", *qtd);
return turma;
}
void salvarDados(Aluno *turma, int qtd) {
if (turma == NULL || qtd == 0) return;
FILE *arq = fopen("alunos.dat", "wb");
if (arq != NULL) {
fwrite(turma, sizeof(Aluno), qtd, arq);
fclose(arq);
printf(">> %d registro(s) salvo(s) em 'alunos.dat' com sucesso!\n", qtd);
}
}
Aluno* cadastrarAluno(Aluno *turma, int *qtd) {
int novaQtd = *qtd + 1;
Aluno *temp = (Aluno*) realloc(turma, novaQtd * sizeof(Aluno));
if (temp == NULL) {
printf(">> ERRO: Memoria insuficiente!\n");
return turma;
}
turma = temp;
printf("\n--- CADASTRAR NOVO ALUNO ---\n");
printf("Matricula (numero): ");
scanf("%d", &turma[*qtd].matricula);
printf("Nome do Aluno: ");
scanf(" %[^\n]", turma[*qtd].nome);
printf("Media Final: ");
scanf("%f", &turma[*qtd].media);
*qtd = novaQtd;
printf(">> Aluno cadastrado com sucesso!\n");
return turma;
}
void listarAlunos(Aluno *turma, int qtd) {
if (turma == NULL || qtd == 0) {
printf("\n>> Nenhum aluno cadastrado no momento.\n");
return;
}
printf("\n==========================================================\n");
printf(" MATRICULA | NOME | MEDIA FINAL \n");
printf("==========================================================\n");
for (int i = 0; i < qtd; i++) {
printf(" %-9d | %-30s | %.2f\n", turma[i].matricula, turma[i].nome, turma[i].media);
}
printf("==========================================================\n");
}
void buscarPorMatricula(Aluno *turma, int qtd) {
if (turma == NULL || qtd == 0) {
printf("\n>> Lista vazia.\n");
return;
}
int matBusca;
printf("\nDigite a matricula para busca: ");
scanf("%d", &matBusca);
for (int i = 0; i < qtd; i++) {
if (turma[i].matricula == matBusca) {
printf(">> ENCONTRADO: %s | Media: %.2f\n", turma[i].nome, turma[i].media);
return;
}
}
printf(">> Aluno com matricula %d nao foi encontrado.\n", matBusca);
}⚔️ 3. Desafios Práticos (Exercícios 30/50/20)
🥉 Nível Bronze (Fixação)
- Qual a função de
realloc()no métodocadastrarAluno()acima? - Por que é necessário chamar
free(turma)ao final domain()?
🥈 Nível Prata (Aplicação)
- Adicione ao projeto a opção
5. Calcular Média Geral da Turmano menu, que percorre a lista de alunos e exibe a média aritmética das notas da turma inteira.
🥇 Nível Ouro (Desafio)
- Implemente a função de ordenação
void ordenarPorMedia(Aluno *turma, int qtd)utilizando o algoritmo BubbleSort para ordenar a lista de alunos em ordem decrescente de nota antes de exibi-los na tela.
💡 Gabaritos Sanfonados de Resposta
💡 Ver Gabarito do Nível Bronze
- O
realloc()expande dinamicamente a memória do vetorturmaalocado no Heap para comportar mais 1 registro sem perder os cadastros anteriores. - O
free(turma)devolve a memória Heap alocada dinamicamente ao Sistema Operacional, evitando vazamentos de memória (Memory Leak).
💡 Ver Gabarito do Nível Prata & Ouro (Código C)
void ordenarPorMedia(Aluno *turma, int qtd) {
for (int i = 0; i < qtd - 1; i++) {
for (int j = 0; j < qtd - i - 1; j++) {
if (turma[j].media < turma[j+1].media) {
Aluno temp = turma[j];
turma[j] = turma[j+1];
turma[j+1] = temp;
}
}
}
printf(">> Alunos ordenados por media com sucesso!\n");
}📚 Referências Teóricas Oficiais
- KERNIGHAN, Brian W.; RITCHIE, Dennis M.. A Linguagem de Programação C. 2ª Edição. Campus, 1989. Cap. 8 (Interface com o Sistema Operacional).
- CORMEN, Thomas H. et al.. Algoritmos: Teoria e Prática. 3ª Edição. Elsevier, 2012.
⬅️ Capítulo Anterior (Tabelas Hash) | Voltar ao Sumário da Fase 01 | Avançar para a Fase 02 (Linux & Git) ➡️