🚀 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:

  1. Integrar todos os conceitos da Fase 01 (Sintaxe C, Controle de Fluxo, Funções, Structs, Ponteiros, Alocação Dinâmica e Arquivos).
  2. Construir um Sistema de Gestão em Linha de Comando (CLI) completo e modular.
  3. Garantir a persistência dos dados salvando e recarregando registros em arquivo binário (alunos.dat).
  4. 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)

  1. Qual a função de realloc() no método cadastrarAluno() acima?
  2. Por que é necessário chamar free(turma) ao final do main()?

🥈 Nível Prata (Aplicação)

  1. Adicione ao projeto a opção 5. Calcular Média Geral da Turma no menu, que percorre a lista de alunos e exibe a média aritmética das notas da turma inteira.

🥇 Nível Ouro (Desafio)

  1. 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
  1. O realloc() expande dinamicamente a memória do vetor turma alocado no Heap para comportar mais 1 registro sem perder os cadastros anteriores.
  2. 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) ➡️