⚡ Cap 14: Aritmética de Ponteiros, Ponteiros Duplos e Function Pointers
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().
🗺️ 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çãoptr + 1resulta em0x1004(em plataformas ondesizeof(int) == 4). - Se
double *dptr = 0x1000, a operaçãodptr + 1resulta em0x1008(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
voidnão possui tamanho (sizeof(void)é indefinido), é estritamente proibido desreferenciar ou fazer aritmética em umvoid*diretamente. É obrigatório realizar um casting explícito antes da operação:void *ptr = № 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):
- Defina o tipo de função de callback:
typedef double (*OperacaoMatematica)(double, double);. - Crie as funções:
somar,subtrair,multiplicaredividir. - Crie uma estrutura
Comandocontendo o caractere do operador ('+','-','*','/') e o respectivo ponteiro para a função. - Crie uma função
double despacharOperacao(char op, double a, double b)que percorra a tabela e execute o callback correspondente instantaneamente sem nenhum blocoswitch-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