Skip to content

Instantly share code, notes, and snippets.

@dimatefps
Last active November 13, 2025 19:15
Show Gist options
  • Select an option

  • Save dimatefps/92bbf4744cecf1980b4ad497a50f4096 to your computer and use it in GitHub Desktop.

Select an option

Save dimatefps/92bbf4744cecf1980b4ad497a50f4096 to your computer and use it in GitHub Desktop.
Listas ligadas

Ótimo, Mate. O PDF que o professor Renzo disponibilizou é uma introdução clara às listas ligadas, que são uma estrutura fundamental em C. Vamos entender o conceito e o que cada parte do material significa.


1. Por que listas ligadas existem

O professor começa comparando com vetores:

  • Em um vetor, os elementos ficam lado a lado na memória (contíguos).

  • Isso é bom para acesso rápido, mas ruim se você precisar mudar o tamanho.

    • Se o vetor for pequeno, pode faltar espaço.
    • Se for grande, você desperdiça memória.
  • E mesmo que haja espaço livre, pode não haver um bloco contínuo do tamanho que você precisa.

A lista ligada resolve isso: Cada elemento (chamado ) é alocado dinamicamente, e um nó aponta para o próximo. Assim, os elementos não precisam estar juntos na memória.


2. Estrutura de um nó

Em C, o nó é definido assim:

typedef struct no *p_no;

struct no {
    int dado;    // valor armazenado
    p_no prox;   // ponteiro para o próximo nó
};

Aqui:

  • p_no é um apelido para struct no * (ou seja, um ponteiro para nó).

  • Cada nó contém:

    • um valor (dado);
    • um ponteiro para o próximo nó (prox).

Se prox for NULL, significa que aquele é o último nó da lista.


3. Criar e destruir uma lista

Criar:

p_no criar_lista() {
    return NULL;
}

Uma lista vazia é representada por NULL, pois ainda não há nós.

Destruir (versão recursiva):

void destruir_lista(p_no lista) {
    if (lista != NULL) {
        destruir_lista(lista->prox);
        free(lista);
    }
}

Aqui:

  • A função chama a si mesma para liberar todos os nós.
  • Quando chega no último (prox == NULL), libera a memória de volta.

4. Adicionar um elemento

p_no adicionar_elemento(p_no lista, int valor) {
    p_no novo = malloc(sizeof(struct no));
    novo->dado = valor;
    novo->prox = lista;
    return novo;
}

O que acontece:

  1. Aloca memória para um novo nó.
  2. Guarda o valor nele.
  3. Faz o novo nó apontar para a lista antiga.
  4. Retorna o ponteiro para o novo nó, que passa a ser o início da lista.

Exemplo:

lista = adicionar_elemento(lista, 10);
lista = adicionar_elemento(lista, 20);
lista = adicionar_elemento(lista, 30);

A memória fica assim:

[30] -> [20] -> [10] -> NULL

5. Imprimir elementos

Forma iterativa:

void imprime(p_no lista) {
    p_no atual;
    for (atual = lista; atual != NULL; atual = atual->prox)
        printf("%d\n", atual->dado);
}

Forma recursiva:

void imprime_recursivo(p_no lista) {
    if (lista != NULL) {
        printf("%d\n", lista->dado);
        imprime_recursivo(lista->prox);
    }
}

Ambas percorrem a lista até encontrar NULL.


6. Buscar um elemento

p_no busca(p_no lista, int x) {
    if (lista == NULL || lista->dado == x)
        return lista;
    return busca(lista->prox, x);
}

Ela retorna o ponteiro para o nó que contém o valor x, ou NULL se não existir.


7. Remover elementos

Primeira ocorrência:

p_no remove(p_no lista, int x) {
    if (lista == NULL) return NULL;
    if (lista->dado == x) {
        p_no proximo = lista->prox;
        free(lista);
        return proximo;
    }
    lista->prox = remove(lista->prox, x);
    return lista;
}

Todas as ocorrências:

p_no remove_todos(p_no lista, int x) {
    if (lista == NULL) return NULL;
    lista->prox = remove_todos(lista->prox, x);
    if (lista->dado == x) {
        p_no proximo = lista->prox;
        free(lista);
        return proximo;
    }
    return lista;
}

8. Exemplo completo do programa

#include <stdio.h>
#include <stdlib.h>

typedef struct no *p_no;

struct no {
    int dado;
    p_no prox;
};

p_no criar_lista() {
    return NULL;
}

p_no adicionar_elemento(p_no lista, int valor) {
    p_no novo = malloc(sizeof(struct no));
    novo->dado = valor;
    novo->prox = lista;
    return novo;
}

void imprime(p_no lista) {
    for (p_no atual = lista; atual != NULL; atual = atual->prox)
        printf("%d ", atual->dado);
    printf("\n");
}

void destruir_lista(p_no lista) {
    if (lista != NULL) {
        destruir_lista(lista->prox);
        free(lista);
    }
}

int main() {
    int num;
    p_no lista = criar_lista();

    printf("Digite números positivos (0 para parar): ");
    scanf("%d", &num);
    while (num > 0) {
        lista = adicionar_elemento(lista, num);
        scanf("%d", &num);
    }

    imprime(lista);
    destruir_lista(lista);
    return 0;
}

9. Visualizando mentalmente

Se você digitar:

10 20 30 0

A lista fica:

[30] -> [20] -> [10] -> NULL

Porque cada novo elemento é adicionado no início.


Boa pergunta, Mate. O operador -> em C é usado para acessar campos de uma struct por meio de um ponteiro.

Vamos entender isso em partes:


1. Quando você tem uma struct comum

Exemplo:

struct no {
    int dado;
    struct no *prox;
};

struct no n;
n.dado = 10;

Aqui, n não é um ponteiro, é a própria struct. Então, para acessar o campo dado, usamos o ponto (.): n.dado.


2. Quando você tem um ponteiro para struct

struct no *p;
p = &n;

Agora p não é a struct em si, e sim um endereço que aponta para ela.

Para acessar dado, primeiro precisaríamos “seguir” o ponteiro:

(*p).dado = 10;

O que isso significa:

  • *p acessa o conteúdo apontado (a struct em si);
  • (*p).dado acessa o campo dado dessa struct.

3. O operador ->

O -> é apenas uma forma abreviada de escrever (*p).campo.

Essas duas linhas são iguais:

(*p).dado = 10;
p->dado = 10;

4. No caso do seu código

novo->dado = valor;
novo->prox = lista;
  • novo é um ponteiro para uma struct do tipo no, criada com malloc.
  • Então novo->dado quer dizer “o campo dado da struct apontada por novo”.

Se você tentasse usar novo.dado, daria erro, porque novo não é a struct em si, é um ponteiro.


5. Resumo prático

Situação Operador Exemplo
Você tem a struct diretamente . n.dado
Você tem um ponteiro para struct -> p->dado
p->campo é o mesmo que (*p).campo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment