Bem-vindo ao décimo quarto capítulo da Especialização em Engenharia de Sistemas com Linguagem C (C17/C23)! ⚡

A verdadeira maestria na programação de sistemas exige ir além dos ponteiros simples. Neste capítulo, você dominará como a CPU calcula deslocamentos em bytes através da Aritmética de Ponteiros, como alterar ponteiros remotamente com Ponteiros Duplos (**ptr), a versatilidade de ponteiros genéricos void* e o poder arquitetural de Ponteiros para Funções (Function Pointers) para implementar Callbacks, polimorfismo e a função canônica de ordenação qsort().

Aritmética de Ponteiros, Ponteiros Duplos e Function Pointers em C


🗺️ Mapa Conceitual do Capítulo

graph TD
    A["Ponteiros Avançados em C"] --> B["1. Aritmética de Ponteiros"]
    A --> C["2. Ponteiros Duplos (**ptr)"]
    A --> D["3. Ponteiro Genérico void*"]
    A --> E["4. Function Pointers & Callbacks"]

    B --> B1["ptr + n salta n * sizeof(T) bytes | *(p + i) == p[i]"]
    C --> C1["Indireção de segundo nível e modificação remota de ponteiros"]
    D --> D1["Abstração genérica de dados e conversão explícita de tipos"]
    E --> E1["Ponteiros para código .text, Dispatch Tables e qsort()"]

📐 1. Aritmética de Ponteiros e a Equivalência Canônica

Aritmética de ponteiros não opera em bytes absolutos, mas em unidades do tipo de dado apontado:

\[\text{Novo Endereco} = \text{Endereco Base} + \big( n \times \text{sizeof}(\text{tipo}) \big)\]
  • Se int *ptr = 0x1000, a operação ptr + 1 resulta em 0x1004 (em plataformas onde sizeof(int) == 4).
  • Se double *dptr = 0x1000, a operação dptr + 1 resulta em 0x1008 (sizeof(double) == 8).

A Equivalência Universal do C:

\(\mathbf{*(ptr + i) \equiv ptr[i]}\)

Qualquer acesso com colchetes arr[i] é convertido pelo compilador diretamente para desreferenciação com deslocamento *(arr + i)!

Subtração de Ponteiros (ptrdiff_t):

A subtração de dois ponteiros do mesmo tipo (p2 - p1) retorna a quantidade de elementos entre eles (do tipo padronizado ptrdiff_t definido em <stddef.h>).


🎯 2. Ponteiros Duplos (**ptr - Ponteiro para Ponteiro)

Um Ponteiro Duplo é uma variável que armazena o endereço de memória de outro ponteiro:

int valor = 42;
int *ptr = &valor;    // ptr aponta para valor
int **pptr = &ptr;    // pptr aponta para ptr

printf("Valor: %d\n", **pptr); // Desreferenciação dupla -> 42

Para que servem Ponteiros Duplos?

São essenciais quando uma função precisa alterar para onde o ponteiro do chamador está apontando (ex: alocar buffers dinâmicos, alterar o nó raiz de uma árvore ou manipular matrizes dinâmicas).


🧰 3. Ponteiro Genérico (void*)

Um ponteiro do tipo void* é um ponteiro universal capaz de armazenar qualquer endereço de memória sem estar vinculado a um tipo de dado específico.

[!WARNING] Regra do ISO C: Como void não possui tamanho (sizeof(void) é indefinido), é estritamente proibido desreferenciar ou fazer aritmética em um void* diretamente. É obrigatório realizar um casting explícito antes da operação:

void *ptr = &numero;
int valor = *(int*)ptr; // ✅ Casting explícito para int*

⚡ 4. Ponteiros para Funções (Function Pointers) e qsort()

Assim como variáveis residem na memória RAM (Stack/Heap), as funções compiladas residem no segmento de código .text. Um Function Pointer armazena o endereço de início das instruções de uma função:

Sintaxe de Declaração:

// Declara um ponteiro para qualquer função que receba dois ints e retorne um int:
int (*operacao)(int, int);

Callbacks e a Função Padrão qsort():

A função qsort() da <stdlib.h> implementa o algoritmo QuickSort genérico utilizando um Callback de comparação:

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

// Função de Callback para ordenação crescente
int compararInteiros(const void *a, const void *b) {
    int valA = *(const int*)a;
    int valB = *(const int*)b;
    return (valA - valB); // Retorna negativo se valA < valB, 0 se igual, positivo se maior
}

int main(void) {
    int v[] = {50, 10, 80, 20, 90, 30};
    size_t n = sizeof(v) / sizeof(v[0]);

    // Ordenação de alta performance em O(N log N)
    qsort(v, n, sizeof(int), compararInteiros);

    for (size_t i = 0; i < n; i++) printf("%d ", v[i]);
    printf("\n");
    return 0;
}

🔍 5. Diagnóstico & Resolução de Problemas (Troubleshooting)

Sintoma Observado Causa Provável Como Resolver
Erro de compilação: warning: dereferencing 'void *' pointer Tentativa de acessar *ptr em uma variável do tipo void*. Realize a conversão explícita de tipo: *(int*)ptr.
qsort ordena o vetor de forma invertida ou caótica O comparador de qsort sofreu overflow na subtração valA - valB ou comparou ponteiros em vez de valores. Use comparações explícitas com if para valores grandes (if (a > b) return 1; if (a < b) return -1; return 0;).
Ponteiro duplo não reflete alteração no chamador A função foi declarada com apenas um asterisco int *ptr em vez de int **ptr. Adicione a indireção dupla para permitir que a função altere o endereço original.

🏆 6. Desafio Prático de Consolidação

Enunciado do Desafio: Desenvolva um programa em C chamado tabela_despacho_callbacks.c que implemente um Despachante Dinâmico de Comandos (Dispatch Table):

  1. Defina o tipo de função de callback: typedef double (*OperacaoMatematica)(double, double);.
  2. Crie as funções: somar, subtrair, multiplicar e dividir.
  3. Crie uma estrutura Comando contendo o caractere do operador ('+', '-', '*', '/') e o respectivo ponteiro para a função.
  4. Crie uma função double despacharOperacao(char op, double a, double b) que percorra a tabela e execute o callback correspondente instantaneamente sem nenhum bloco switch-case.
🔍 Ver Solução Comentada do Desafio
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int id;
    double nota;
} Aluno;

int compararAlunosDesc(const void *a, const void *b) {
    const Aluno *alunoA = (const Aluno*)a;
    const Aluno *alunoB = (const Aluno*)b;
    if (alunoB->nota > alunoA->nota) return 1;
    if (alunoB->nota < alunoA->nota) return -1;
    return 0;
}

int main(void) {
    Aluno turma[] = { {101, 7.5}, {102, 9.8}, {103, 5.2}, {104, 8.9} };
    size_t n = sizeof(turma) / sizeof(turma[0]);

    qsort(turma, n, sizeof(Aluno), compararAlunosDesc);

    printf("Ranking de Alunos Ordenado por Callback qsort:\n");
    for (size_t i = 0; i < n; i++) {
        printf(" #%zu: ID %d | Nota: %.1lf\n", i + 1, turma[i].id, turma[i].nota);
    }

    return 0;
}

🧭 Navegação Rápida

| 📖 Teoria | 📊 Slides | 🧠 Quiz | 💻 Exemplos | 🧩 Exercícios | | :— | :— | :— | :— | :— | | Ler Teoria | Ver Slides | Fazer Quiz | Ver Exemplos | Praticar Exercícios |


🧭 Navegação do Capítulo: ⬅️ Capítulo Anterior · 📚 Sumário do Módulo · ➡️ Próximo Capítulo