Implementação de Listas Ligadas utilizando Cursores em C

Em ambientes onde a alocação dinâmica de memória não é permitida ou disponível, podemos simular o comportamento de ponteiros e listas ligadas utilizando uma estrutura conhecida como Cursor. Essa técnica utiliza um array global de estruturas para gerecniar a memória manualmente, onde os índices do array atuam como endereços de memória.

Definição da Estrutura e Interface

Para implementar um sistema baseado em cursores, definimos um array de estruturas onde cada "nó" contém um campo de dados e um índice para o próximo elemento. O índice 0 geralmente é reservado para atuar como a cabeça de uma lista de blocos livres.

#ifndef CURSOR_H
#define CURSOR_H

#define CAPACIDADE 50
#define CURSOR_NULO 0

typedef int Posicao;
typedef Posicao Lista;

typedef struct {
    int Conteudo;
    Posicao Proximo;
} Nodo;

// Pool de memória global simulado
extern Nodo EspacoCursor[CAPACIDADE];

void InicializarGerenciador();
Posicao AlocarNodo();
void DesalocarNodo(Posicao p);

Lista CriarLista();
int VerificarVazia(const Lista L);
Posicao Buscar(int valor, const Lista L);
void InserirElemento(int valor, Lista L, Posicao p);
void Remover(int valor, Lista L);
Posicao BuscarAnterior(int valor, const Lista L);

#endif

Implementação do Gerenciamento de Memória

A lógica central reside em manter uma lista encadeada de nós disponíveis dentro do array. Quando um novo nó é necessário, nós o removemos da lista de "espaço livre". Quando um nó é excluído, ele retorna para essa lista.

#include "cursor.h"
#include <stdio.h>

Nodo EspacoCursor[CAPACIDADE];

// Inicializa o array encadeando todos os elementos na lista de livres
void InicializarGerenciador() {
    for (int i = 0; i < CAPACIDADE - 1; i++) {
        EspacoCursor[i].Conteudo = 0;
        EspacoCursor[i].Proximo = i + 1;
    }
    EspacoCursor[CAPACIDADE - 1].Proximo = CURSOR_NULO;
}

Posicao AlocarNodo() {
    Posicao p = EspacoCursor[0].Proximo;
    if (p == CURSOR_NULO) {
        fprintf(stderr, "Erro: Memória insuficiente no pool.\n");
        return CURSOR_NULO;
    }
    EspacoCursor[0].Proximo = EspacoCursor[p].Proximo;
    return p;
}

void DesalocarNodo(Posicao p) {
    EspacoCursor[p].Conteudo = 0;
    EspacoCursor[p].Proximo = EspacoCursor[0].Proximo;
    EspacoCursor[0].Proximo = p;
}

Lista CriarLista() {
    Lista L = AlocarNodo();
    if (L != CURSOR_NULO) {
        EspacoCursor[L].Proximo = CURSOR_NULO;
    }
    return L;
}

int VerificarVazia(const Lista L) {
    return EspacoCursor[L].Proximo == CURSOR_NULO;
}

Manipulação da Lista de Dados

As funções de busca e inserção seguem a lógica clássica de listas ligadas, mas substituindo o acesso a ponteiros (->next) pelo acesso ao índice do array (EspacoCursor[p].Proximo).

Posicao Buscar(int valor, const Lista L) {
    Posicao p = EspacoCursor[L].Proximo;
    while (p != CURSOR_NULO && EspacoCursor[p].Conteudo != valor) {
        p = EspacoCursor[p].Proximo;
    }
    return p;
}

Posicao BuscarAnterior(int valor, const Lista L) {
    Posicao p = L;
    while (EspacoCursor[p].Proximo != CURSOR_NULO && 
           EspacoCursor[EspacoCursor[p].Proximo].Conteudo != valor) {
        p = EspacoCursor[p].Proximo;
    }
    return p;
}

void InserirElemento(int valor, Lista L, Posicao p) {
    Posicao novo = AlocarNodo();
    if (novo == CURSOR_NULO) return;

    EspacoCursor[novo].Conteudo = valor;
    EspacoCursor[novo].Proximo = EspacoCursor[p].Proximo;
    EspacoCursor[p].Proximo = novo;
}

void Remover(int valor, Lista L) {
    Posicao anterior = BuscarAnterior(valor, L);
    if (EspacoCursor[anterior].Proximo != CURSOR_NULO) {
        Posicao temp = EspacoCursor[anterior].Proximo;
        EspacoCursor[anterior].Proximo = EspacoCursor[temp].Proximo;
        DesalocarNodo(temp);
    }
}

Exemplo de Utilização

O código abaixo demonstra como inicializar o pool e realizar operações básicas. Um ponto crítico em C é garantir que variáveis globais ou arrays compartilhados declarados em headers usem o qualificador extern para evitar erros de múltipla definição durante a linkagem.

#include <stdio.h>
#include "cursor.h"

int main() {
    InicializarGerenciador();
    
    Lista minhaLista = CriarLista();
    
    if (VerificarVazia(minhaLista)) {
        printf("Lista inicializada corretamente.\n");
    }

    // Inserindo elementos
    InserirElemento(10, minhaLista, minhaLista);
    InserirElemento(20, minhaLista, minhaLista);
    
    Posicao pos = Buscar(10, minhaLista);
    if (pos != CURSOR_NULO) {
        printf("Elemento 10 encontrado na posicao: %d\n", pos);
    }

    Remover(10, minhaLista);
    printf("Apos remover 10, proximo do header: %d\n", EspacoCursor[minhaLista].Proximo);

    return 0;
}

A implementação baseada em cursores é uma excelente forma de entender a abstração da memória. Ao tratar índices como endereços, o desenvolvedor ganha controle total sobre a disposição dos dados e evita a fragmentação comum em alocações dinâmicas de pequena escala.

Tags: C DataStructures MemoryManagement algorithms

Publicado em 6-7 03:01 por Thomas