Este artigo detalha a implementação de um sistema de gerenciamento de agenda em C, focado em funcionalidades essenciais e no uso de alocação dinâmica de memória.
- Análise de Requisitos
O objetivo é desenvolver um sistema de agenda em C que permita adicionar, remover, buscar, modificar, limpar e ordenar contatos. A característica principal é a utilização de alocação dinâmica de memória para gerenciar o armazenamento dos contatos.
- Arquitetura do Programa
O projeto é estruturado em três componentes:
test.c: Contém a função principal (main) e a interface do usuário.contact.c: Implementa a lógica e as funcionalidades das operações da agenda.contact.h: Arquivo de cabeçalho que declara estruturas de dados, constantes e protótipos de funções.
- Detalhamento da Implementação das Funções
3.1. Função Principal (main)
A função main inicialiaz a estrutura da agenda, exibe um menu interativo e processa as escolhas do usuário através de um loop do-while e uma estrutura switch. O loop continua enquanto o usuário não escolher a opção de sair (EXIT).
A agenda é representada pela estrutura struct contact, que armazena um ponteiro para os dados dos contatos (data), o número atual de contatos (size) e a capacidade total alocada (capacity). Esta abordagem difere de um array estático, permitindo o redimensionamento dinâmico.
Exemplo de código (main):
int main() {
struct contact agenda;
Init_Contact(&agenda); // Inicializa a agenda
int choice = 0;
do {
Display_Menu(); // Mostra o menu
printf("Escolha uma opção -> ");
scanf("%d", &choice);
switch (choice) {
case ADD_OPTION:
Add_Contact(&agenda);
break;
case DELETE_OPTION:
Delete_Contact(&agenda);
break;
case SEARCH_OPTION:
Search_Contact(&agenda);
break;
case MODIFY_OPTION:
Modify_Contact(&agenda);
break;
case SHOW_OPTION:
Display_Contacts(&agenda);
break;
case CLEAR_OPTION:
Clear_Contacts(&agenda);
break;
case EXIT_OPTION:
Destroy_Contact(&agenda);
printf("Saindo da agenda. Adeus!\n");
break;
default:
printf("Opção inválida. Tente novamente!\n");
}
} while (choice != EXIT_OPTION);
return 0;
}
3.2. Função de Menu (Display_Menu)
Esta função simplesmente imprime as opções disponíveis para o usuário na tela, formatadas para clareza.
Exemplo de código (Display_Menu):
void Display_Menu() {
printf("************************************\n");
printf("**** 1. Adicionar 2. Deletar ****\n");
printf("**** 3. Buscar 4. Modificar ****\n");
printf("**** 5. Mostrar 6. Limpar ****\n");
printf("**** 7. Ordenar 0. Sair ****\n");
printf("************************************\n");
}
3.3. Inicialização da Agenda (Init_Contact)
A função Init_Contact aloca memória inicial para os contatos usando malloc. Um tamanho inicial é definido (por exemplo, 3 contatos). Se a alocação falhar, a função retorna. O size é inicializado como 0 e a capacity é definida para o tamanho inicial alocado.
Exemplo de código (Init_Contact):
void Init_Contact(struct contact *agenda_ptr) {
agenda_ptr->data = (struct PersonInfo *)malloc(sizeof(struct PersonInfo) * INITIAL_CAPACITY);
if (agenda_ptr->data == NULL) {
printf("Erro de alocação de memória inicial.\n");
return;
}
agenda_ptr->size = 0;
agenda_ptr->capacity = INITIAL_CAPACITY;
}
3.4. Adicionar Contato (Add_Contact)
Antes de adicionar um novo contato, a função CheckCapacity é chamada. Se a agenda estiver cheia (size == capacity), a capacidade é aumentada usando realloc. Após garantir espaço suficiente, os detalhes do novo contato (nome, idade, etc.) são lidos do usuário e armazenados na próxima posição disponível. O size é então incrementado.
Exemplo de código (Add_Contact):
// Função auxiliar para verificar e redimensionar a capacidade
void Ensure_Capacity(struct contact *agenda_ptr) {
if (agenda_ptr->size == agenda_ptr->capacity) {
struct PersonInfo *temp_ptr = realloc(agenda_ptr->data, (agenda_ptr->capacity + CAPACITY_INCREMENT) * sizeof(struct PersonInfo));
if (temp_ptr != NULL) {
agenda_ptr->data = temp_ptr;
agenda_ptr->capacity += CAPACITY_INCREMENT;
printf("Capacidade aumentada com sucesso!\n");
} else {
printf("Falha ao aumentar a capacidade.\n");
}
}
}
void Add_Contact(struct contact *agenda_ptr) {
Ensure_Capacity(agenda_ptr);
printf("Digite o nome: ");
scanf("%s", agenda_ptr->data[agenda_ptr->size].name);
printf("Digite a idade: ");
scanf("%d", &agenda_ptr->data[agenda_ptr->size].age);
printf("Digite o sexo: ");
scanf("%s", agenda_ptr->data[agenda_ptr->size].sex);
printf("Digite o telefone: ");
scanf("%s", agenda_ptr->data[agenda_ptr->size].phone);
printf("Digite o endereco: ");
scanf("%s", agenda_ptr->data[agenda_ptr->size].address);
agenda_ptr->size++;
printf("Contato adicionado com sucesso!\n");
}
3.5. Remover Contato (Delete_Contact)
A função solicita o nome do contato a ser removido. Utiliza uma função auxiliar (Find_Contact_Index_ByName) para localizar o índice do contato. Se encontrado, os elementos subsequentes são deslocados uma posição para a esquerda para preencher a lacuna, e o size é decrementado.
Exemplo de código (Delete_Contact):
void Delete_Contact(struct contact *agenda_ptr) {
char name_to_delete[MAX_NAME_LEN];
printf("Digite o nome do contato a ser deletado: ");
scanf("%s", name_to_delete);
int index = Find_Contact_Index_ByName(agenda_ptr, name_to_delete);
if (index == -1) {
printf("Contato não encontrado.\n");
} else {
for (int i = index; i < agenda_ptr->size - 1; ++i) {
agenda_ptr->data[i] = agenda_ptr->data[i + 1];
}
agenda_ptr->size--;
printf("Contato deletado com sucesso!\n");
}
}
3.6. Buscar Contato (Search_Contact)
Similar à remoção, esta função busca um contato pelo nome usando Find_Contact_Index_ByName. Se o contato for encontrado, seus detalhes são exibidos formatados.
Exemplo de código (Search_Contact):
void Search_Contact(const struct contact *agenda_ptr) {
char name_to_find[MAX_NAME_LEN];
printf("Digite o nome do contato a ser buscado: ");
scanf("%s", name_to_find);
int index = Find_Contact_Index_ByName(agenda_ptr, name_to_find);
if (index == -1) {
printf("Contato não encontrado.\n");
} else {
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "Nome", "Idade", "Sexo", "Telefone", "Endereco");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
agenda_ptr->data[index].name,
agenda_ptr->data[index].age,
agenda_ptr->data[index].sex,
agenda_ptr->data[index].phone,
agenda_ptr->data[index].address);
}
}
3.7. Função Auxiliar de Busca (Find_Contact_Index_ByName)
Esta função é responsável por percorrer o array de contatos e retornar o índice do primeiro contato cujo nome corresponde ao nome fornecido. Retorna -1 se nenhum contato for encontrado.
Exemplo de código (Find_Contact_Index_ByName):
static int Find_Contact_Index_ByName(const struct contact *agenda_ptr, const char name_to_find[MAX_NAME_LEN]) {
for (int i = 0; i < agenda_ptr->size; ++i) {
if (strcmp(agenda_ptr->data[i].name, name_to_find) == 0) {
return i;
}
}
return -1; // Not found
}
3.8. Modificar Contato (Modify_Contact)
Similar à busca e remoção, encontra o contato pelo nome. Se encontrado, solicita ao usuário os novos detalhes e atualiza o registro correspondente.
Exemplo de código (Modify_Contact):
void Modify_Contact(struct contact *agenda_ptr) {
char name_to_modify[MAX_NAME_LEN];
printf("Digite o nome do contato a ser modificado: ");
scanf("%s", name_to_modify);
int index = Find_Contact_Index_ByName(agenda_ptr, name_to_modify);
if (index == -1) {
printf("Contato não encontrado.\n");
} else {
printf("Digite os novos dados para %s:\n", name_to_modify);
printf("Novo nome: ");
scanf("%s", agenda_ptr->data[index].name);
printf("Nova idade: ");
scanf("%d", &agenda_ptr->data[index].age);
printf("Novo sexo: ");
scanf("%s", agenda_ptr->data[index].sex);
printf("Novo telefone: ");
scanf("%s", agenda_ptr->data[index].phone);
printf("Novo endereco: ");
scanf("%s", agenda_ptr->data[index].address);
printf("Contato modificado com sucesso!\n");
}
}
3.9. Exibir Contatos (Display_Contacts)
Verifica se a agenda está vazia. Se não estiver, imprime um cabeçalho de tabela e, em seguida, itera sobre todos os contatos, exibindo os detalhes de cada um em formato tabular.
Exemplo de código (Display_Contacts):
void Display_Contacts(const struct contact *agenda_ptr) {
if (agenda_ptr->size == 0) {
printf("A agenda está vazia!\n");
} else {
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "Nome", "Idade", "Sexo", "Telefone", "Endereco");
for (int i = 0; i < agenda_ptr->size; ++i) {
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
agenda_ptr->data[i].name,
agenda_ptr->data[i].age,
agenda_ptr->data[i].sex,
agenda_ptr->data[i].phone,
agenda_ptr->data[i].address);
}
}
}
3.10. Ordenar Contatos (Sort_Contacts)
Esta função permite ao usuário escolher um critério de ordenação (nome, idade ou endereço). Utiliza a função qsort da biblioteca padrão C, fornecendo ponteiros para as funções de comparação apropriadas (CompareByName, CompareByAge, CompareByAddress).
Exemplo de código (Sort_Contacts):
// Funções de comparação para qsort
int CompareByName(const void *a, const void *b) {
return strcmp(((struct PersonInfo *)a)->name, ((struct PersonInfo *)b)->name);
}
int CompareByAge(const void *a, const void *b) {
return ((struct PersonInfo *)a)->age - ((struct PersonInfo *)b)->age;
}
int CompareByAddress(const void *a, const void *b) {
return strcmp(((struct PersonInfo *)a)->address, ((struct PersonInfo *)b)->address);
}
void Sort_Contacts(struct contact *agenda_ptr) {
printf("Escolha o critério de ordenação:\n");
printf("1. Por Nome\n2. Por Idade\n3. Por Endereco\n");
int sort_choice;
scanf("%d", &sort_choice);
switch (sort_choice) {
case 1:
qsort(agenda_ptr->data, agenda_ptr->size, sizeof(struct PersonInfo), CompareByName);
printf("Ordenado por nome com sucesso.\n");
break;
case 2:
qsort(agenda_ptr->data, agenda_ptr->size, sizeof(struct PersonInfo), CompareByAge);
printf("Ordenado por idade com sucesso.\n");
break;
case 3:
qsort(agenda_ptr->data, agenda_ptr->size, sizeof(struct PersonInfo), CompareByAddress);
printf("Ordenado por endereco com sucesso.\n");
break;
default:
printf("Opção de ordenação inválida.\n");
}
}
3.11. Limpar Agenda (Clear_Contacts)
Esta função redefine o conteúdo da memória alocada para os contatos (usando memset para zerar) e define size como 0, efetivamente esvaziando a agenda sem liberar a memória alocada (que será reutilizada ou liberada ao final).
Exemplo de código (Clear_Contacts):
void Clear_Contacts(struct contact *agenda_ptr) {
// Poderia usar memset para zerar os dados, mas redefinir size é suficiente para lógica.
// memset(agenda_ptr->data, 0, agenda_ptr->capacity * sizeof(struct PersonInfo));
agenda_ptr->size = 0;
printf("Agenda limpa com sucesso!\n");
}
3.12. Detsruir Agenda (Destroy_Contact)
Libera a memória alocada dinamicamente para os contatos usando free e redefine o ponteiro data para NULL para evitar dangling pointers.
Exemplo de código (Destroy_Contact):
void Destroy_Contact(struct contact *agenda_ptr) {
free(agenda_ptr->data);
agenda_ptr->data = NULL;
agenda_ptr->size = 0;
agenda_ptr->capacity = 0;
}
- Estrutura de Arquivos
4.1. test.c
Contém a enumeração das opções do menu, a função Display_Menu e a função main que orquestra a execução do programa.
Exemplo de código (test.c):
#include "contact.h"
enum MenuOptions {
EXIT_OPTION = 0,
ADD_OPTION = 1,
DELETE_OPTION = 2,
SEARCH_OPTION = 3,
MODIFY_OPTION = 4,
SHOW_OPTION = 5,
CLEAR_OPTION = 6,
SORT_OPTION = 7
};
// ... (funções Display_Menu, main, e chamadas para funções de contact.c) ...
4.2. contact.h
Define constantes para os tamanhos máximos dos campos de texto e o tamanho inicial/incremento da capacidade. Declara a estrutura struct PersonInfo para os dados de cada pessoa e struct contact para a agenda. Inclui declarações de todas as funções de gerenciamento da agenda.
Exemplo de código (contact.h):
#ifndef CONTACT_H
#define CONTACT_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME_LEN 20
#define MAX_PHONE_LEN 12
#define MAX_SEX_LEN 5
#define MAX_ADDRESS_LEN 30
#define INITIAL_CAPACITY 3
#define CAPACITY_INCREMENT 2
typedef struct {
char name[MAX_NAME_LEN];
int age;
char sex[MAX_SEX_LEN];
char phone[MAX_PHONE_LEN];
char address[MAX_ADDRESS_LEN];
} PersonInfo;
typedef struct {
PersonInfo *data;
int size;
int capacity;
} ContactAgenda;
// Function Declarations
void Init_Contact(ContactAgenda *agenda_ptr);
void Add_Contact(ContactAgenda *agenda_ptr);
void Delete_Contact(ContactAgenda *agenda_ptr);
void Search_Contact(const ContactAgenda *agenda_ptr);
void Modify_Contact(ContactAgenda *agenda_ptr);
void Display_Contacts(const ContactAgenda *agenda_ptr);
void Sort_Contacts(ContactAgenda *agenda_ptr);
void Clear_Contacts(ContactAgenda *agenda_ptr);
void Destroy_Contact(ContactAgenda *agenda_ptr);
void Display_Menu(); // Moved declaration here
#endif // CONTACT_H
</string.h></stdlib.h></stdio.h>
4.3. contact.c
Implementa todas as funções declaradas em contact.h, incluindo as funções auxiliares como Find_Contact_Index_ByName e Ensure_Capacity, além das funções de comparação para qsort.