📚 Capítulo do curso: 14 — Aritmética de Ponteiros, Ponteiros Duplos e Function Pointers da Especialização em Sistemas com C. Leia o capítulo antes de começar.

🎛️ Miniprojeto C 14 — Tabela de Despacho com Ponteiros de Função

Arquitetura orientada a eventos e callbacks com ponteiros para funções.

⏱️ Tempo estimado 📦 Você vai entregar ✅ Pronto quando
55 min main.c as 3 operações saem pela tabela, a divisão por zero não derruba o programa e o qsort ordena

Diagrama do 🎛️ Miniprojeto C 14 — Tabela de Despacho com Ponteiros de Função

🎯 Objetivos

📋 O problema

Você está construindo o núcleo de uma calculadora de linha de comando cujo motor de operações precisa ser extensível sem editar uma cadeia gigante de if/else. A solução clássica em C é uma tabela de despacho: um array onde cada posição guarda um ponteiro para a função que implementa aquela operação. Adicionar uma operação nova vira “adicionar uma linha na tabela”, não “adicionar mais um else if”.

Seu programa (main.c) deve:

  1. Implementar as 4 operações aritméticas (soma, subtrai, multiplica, divide), todas com a assinatura double f(double, double).
  2. Montar uma tabela de ponteiros de função com as 4 operações, indexada por posição (0=soma, 1=subtrai, 2=multiplica, 3=divide), junto com um array paralelo de nomes para exibição.
  3. Executar 3 operações fixas pré-definidas (índice + dois operandos) chamando a função através da tabela, nunca por nome direto nem por if/switch.
  4. Tratar divisão por zero dentro da própria função divide, sem travar o programa.
  5. Demonstrar qsort() ordenando um array de inteiros fixo, usando uma função de comparação escrita por você.
  6. Demonstrar aritmética de ponteiros, percorrendo o array ordenado com *(ptr + i) em vez de ptr[i].
ID Requisito
RF01 O programa deve compilar sem nenhum aviso com gcc -Wall -Wextra -Wpedantic -std=c17.
RF02 Deve definir as funções soma, subtrai, multiplica e divide, todas com assinatura double f(double, double).
RF03 Deve declarar um array de ponteiros de função (double (*tabela[4])(double, double)) contendo as 4 operações, e um array paralelo const char *nomes[4] com seus nomes.
RF04 Deve executar pelo menos 3 operações fixas indexando a tabela (ex: tabela[indice](a, b)), nunca com if/switch para escolher a operação.
RF05 divide deve detectar denominador 0.0, imprimir um aviso e retornar 0.0 sem crashar o programa.
RF06 Deve ordenar um array de pelo menos 8 inteiros com qsort(), usando uma função de comparação int comparar(const void *a, const void *b) escrita por você.
RF07 Deve imprimir o array ordenado percorrendo-o com aritmética de ponteiros (*(ptr + i)), não com array[i].
graph LR
    A["main()"] --> B["Monta tabela_operacoes[4]\n(function pointers)"]
    B --> C["Executa 3 operacoes\nvia tabela[indice](a,b)"]
    C --> D["qsort() com\ncomparador customizado"]
    D --> E["Percorre resultado com\naritmetica de ponteiros"]

    style B fill:#0284c7,stroke:#38bdf8,color:#ffffff
    style C fill:#0d9488,stroke:#2dd4bf,color:#ffffff
    style D fill:#7c3aed,stroke:#a78bfa,color:#ffffff
    style E fill:#059669,stroke:#34d399,color:#ffffff

🧰 Ambiente

O mesmo do Miniprojeto 01: gcc 8 ou mais novo. No Windows, rode chcp 65001 se os emojis aparecerem quebrados.


Passo 1 — Compilar o esqueleto

🎯 Objetivo: partir de um código que compila e roda, antes de escrever qualquer linha.

⌨️ Faça: crie a pasta c_mini_14, salve o código abaixo como main.c e rode:

gcc -Wall -Wextra -Wpedantic -std=c17 main.c -o app
./app          # Linux/macOS
.\app.exe      # Windows (PowerShell)

📄 src/esqueleto.c

/*
 * Miniprojeto 14: Tabela de Despacho (Dispatch Table) com Function Pointers
 * Especializacao em Engenharia de Sistemas em C
 */
#include <stdio.h>
#include <stdlib.h>

double soma(double a, double b) {
    return a + b;
}

double subtrai(double a, double b) {
    return a - b;
}

double multiplica(double a, double b) {
    return a * b;
}

double divide(double a, double b) {
    // TODO 1: se b for 0.0, imprima um aviso e retorne 0.0 sem crashar.
    return a / b;
}

// TODO 2: escreva a funcao de comparacao para o qsort.
// Assinatura obrigatoria: int comparar_int(const void *a, const void *b);
// Faca cast dos ponteiros void* para "const int *" antes de desreferenciar.

int main(void) {
    printf("====================================================\n");
    printf(" ⚡ Miniprojeto 14: Tabela de Despacho (Dispatch Table)\n");
    printf("====================================================\n");

    // TODO 3: declare "double (*tabela_operacoes[4])(double, double)"
    // e inicialize com {soma, subtrai, multiplica, divide}.

    // TODO 4: declare "const char *nomes_operacoes[4]" com os nomes na
    // mesma ordem da tabela.

    // TODO 5: execute 3 operacoes fixas indexando a tabela, por exemplo:
    // tabela_operacoes[0](10.0, 5.0) -> soma
    // tabela_operacoes[2](4.0, 6.0)  -> multiplica
    // tabela_operacoes[3](9.0, 0.0)  -> divide por zero (deve ser tratado)

    printf("\n--- Ordenando array com qsort() e comparador customizado ---\n");
    int numeros[8] = {42, -7, 15, 3, 99, -1, 0, 23};
    size_t total_numeros = sizeof(numeros) / sizeof(numeros[0]);

    printf("Antes:  ");
    for (size_t i = 0; i < total_numeros; i++) {
        printf("%d ", numeros[i]);
    }
    printf("\n");

    // TODO 6: chame qsort(numeros, total_numeros, sizeof(int), comparar_int);

    printf("\n--- Percorrendo array via aritmetica de ponteiros ---\n");
    // TODO 7: percorra "numeros" usando um ponteiro int *ptr = numeros;
    // e imprima cada elemento com *(ptr + i), nunca com numeros[i].

    printf("====================================================\n");
    printf(" ✅ Status: Operacional e Validado!\n");
    printf("====================================================\n");

    return EXIT_SUCCESS;
}

✅ Checkpoint: zero avisos do gcc, e a saída é:

🖥️ Saída esperada:

====================================================
 ⚡ Miniprojeto 14: Tabela de Despacho (Dispatch Table)
====================================================

--- Ordenando array com qsort() e comparador customizado ---
Antes:  42 -7 15 3 99 -1 0 23 

--- Percorrendo array via aritmetica de ponteiros ---
====================================================
 ✅ Status: Operacional e Validado!
====================================================

Passo 2 — Divisão protegida e a tabela (TODO 1, 3, 4 e 5)

🎯 Objetivo: trocar uma cadeia de if/else por indexação de um array de funções.

⌨️ Faça:

  1. Em divide, se b == 0.0, imprima ` [AVISO] Divisao por zero evitada. Retornando 0.0 e retorne 0.0`.
  2. No main, declare double (*tabela_operacoes[4])(double, double) = {soma, subtrai, multiplica, divide}; e o array paralelo const char *nomes_operacoes[4].
  3. Monte três arrays com as operações fixas (índices {0, 2, 3}, operandos A {10.0, 4.0, 9.0} e B {5.0, 6.0, 0.0}) e, num for, chame tabela_operacoes[idx](a, b).

💡 Leia double (*tabela[4])(double, double) de dentro para fora: tabela é um array de 4 ponteiros para funções que recebem dois double e devolvem double.

✅ Checkpoint: Operacao 'soma'(10.0, 5.0) = 15.00, Operacao 'multiplica'(4.0, 6.0) = 24.00 e, depois do aviso, Operacao 'divide'(9.0, 0.0) = 0.00.

🧯 Se der errado: sem a proteção, 9.0 / 0.0 não derruba o programa (em ponto flutuante, dá inf), mas o resultado sai inf, que não serve para nada. Já a divisão inteira por zero é comportamento indefinido e costuma derrubar o programa.


Passo 3 — qsort com comparador (TODO 2 e 6)

🎯 Objetivo: passar comportamento (a regra de comparação) como parâmetro.

⌨️ Faça: escreva int comparar_int(const void *a, const void *b), faça o cast para const int *, desreferencie e devolva (va > vb) - (va < vb). Chame qsort(numeros, total_numeros, sizeof(int), comparar_int); e imprima Depois:.

✅ Checkpoint: Depois: -7 -1 0 3 15 23 42 99.

🧯 Se der errado: o atalho return va - vb; funciona aqui, mas transborda com valores grandes (por exemplo, INT_MIN - 1) e ordena errado. A forma (va > vb) - (va < vb) nunca transborda.


Passo 4 — Aritmética de ponteiros (TODO 7)

🎯 Objetivo: ver que *(ptr + i) e ptr[i] são a mesma coisa.

⌨️ Faça: int *ptr = numeros; e, num for, imprima *(ptr + %zu) = %d.

✅ Checkpoint: 8 linhas, de *(ptr + 0) = -7 a *(ptr + 7) = 99.


Passo final — Conferir com o gabarito

🎯 Objetivo: validar a saída completa e o código de retorno.

✅ Checkpoint: a saída é esta, e o código de retorno é 0 (echo $? no Linux/macOS ou echo $LASTEXITCODE no PowerShell):

🖥️ Saída esperada:

====================================================
 ⚡ Miniprojeto 14: Tabela de Despacho (Dispatch Table)
====================================================

--- Executando operacoes via tabela de despacho ---
Operacao 'soma'(10.0, 5.0) = 15.00
Operacao 'multiplica'(4.0, 6.0) = 24.00
  [AVISO] Divisao por zero evitada. Retornando 0.0
Operacao 'divide'(9.0, 0.0) = 0.00

--- Ordenando array com qsort() e comparador customizado ---
Antes:  42 -7 15 3 99 -1 0 23 
Depois: -7 -1 0 3 15 23 42 99 

--- Percorrendo array via aritmetica de ponteiros ---
*(ptr + 0) = -7
*(ptr + 1) = -1
*(ptr + 2) = 0
*(ptr + 3) = 3
*(ptr + 4) = 15
*(ptr + 5) = 23
*(ptr + 6) = 42
*(ptr + 7) = 99
====================================================
 ✅ Status: Operacional e Validado!
====================================================

Só abra o gabarito depois de chegar até aqui:

🔑 Ver gabarito: src/main.c

📄 src/main.c

/*
 * Miniprojeto 14: Tabela de Despacho (Dispatch Table) com Function Pointers
 * Especializacao em Engenharia de Sistemas em C
 */
#include <stdio.h>
#include <stdlib.h>

/* As 4 operacoes que a tabela de despacho vai mapear. Todas compartilham
 * a mesma assinatura double(double, double), requisito para entrarem na
 * mesma tabela de ponteiros de funcao. */
double soma(double a, double b) {
    return a + b;
}

double subtrai(double a, double b) {
    return a - b;
}

double multiplica(double a, double b) {
    return a * b;
}

double divide(double a, double b) {
    if (b == 0.0) {
        printf("  [AVISO] Divisao por zero evitada. Retornando 0.0\n");
        return 0.0;
    }
    return a / b;
}

/* Funcao de comparacao exigida pelo qsort: recebe dois "const void *",
 * faz o cast para o tipo real e retorna negativo/zero/positivo. */
int comparar_int(const void *a, const void *b) {
    int va = *(const int *)a;
    int vb = *(const int *)b;
    return (va > vb) - (va < vb);
}

int main(void) {
    printf("====================================================\n");
    printf(" ⚡ Miniprojeto 14: Tabela de Despacho (Dispatch Table)\n");
    printf("====================================================\n");

    /* Tabela de despacho: array de ponteiros de funcao. Em vez de um
     * "if/else" gigante escolhendo a operacao, basta indexar o array. */
    double (*tabela_operacoes[4])(double, double) = {soma, subtrai, multiplica, divide};
    const char *nomes_operacoes[4] = {"soma", "subtrai", "multiplica", "divide"};

    /* 3 operacoes fixas pre-definidas: (indice na tabela, operando a, operando b) */
    int indices[3] = {0, 2, 3};
    double operandos_a[3] = {10.0, 4.0, 9.0};
    double operandos_b[3] = {5.0, 6.0, 0.0};

    printf("\n--- Executando operacoes via tabela de despacho ---\n");
    for (int i = 0; i < 3; i++) {
        int idx = indices[i];
        double resultado = tabela_operacoes[idx](operandos_a[i], operandos_b[i]);
        printf("Operacao '%s'(%.1f, %.1f) = %.2f\n",
               nomes_operacoes[idx], operandos_a[i], operandos_b[i], resultado);
    }

    printf("\n--- Ordenando array com qsort() e comparador customizado ---\n");
    int numeros[8] = {42, -7, 15, 3, 99, -1, 0, 23};
    size_t total_numeros = sizeof(numeros) / sizeof(numeros[0]);

    printf("Antes:  ");
    for (size_t i = 0; i < total_numeros; i++) {
        printf("%d ", numeros[i]);
    }
    printf("\n");

    qsort(numeros, total_numeros, sizeof(int), comparar_int);

    printf("Depois: ");
    for (size_t i = 0; i < total_numeros; i++) {
        printf("%d ", numeros[i]);
    }
    printf("\n");

    printf("\n--- Percorrendo array via aritmetica de ponteiros ---\n");
    /* ptr + i NAO avanca "i bytes": avanca "i * sizeof(int)" bytes,
     * porque o compilador escala o deslocamento pelo tamanho do tipo
     * apontado. *(ptr + i) e' rigorosamente equivalente a ptr[i]. */
    int *ptr = numeros;
    for (size_t i = 0; i < total_numeros; i++) {
        printf("*(ptr + %zu) = %d\n", i, *(ptr + i));
    }

    printf("====================================================\n");
    printf(" ✅ Status: Operacional e Validado!\n");
    printf("====================================================\n");

    return EXIT_SUCCESS;
}

Por que isso importa: a tabela de despacho é o mesmo princípio por trás de vtables de C++, do despacho de syscalls do kernel Linux e de callbacks de bibliotecas gráficas — trocar decisão condicional por indexação de array.


🏆 Desafio

Adicione uma 5ª operação (potencia, calculada com multiplicações sucessivas — sem <math.h>) à tabela sem alterar a lógica do main que a executa, só a tabela e o array de nomes. Em seguida, escreva um segundo comparador para o qsort que ordene os números em ordem decrescente e compare a diferença de uma única linha (return (va > vb) - (va < vb) vira return (va < vb) - (va > vb)).

✅ Checkpoint: tabela_operacoes[4](2.0, 10.0) dá 1024.00, sem mudar o laço que executa as operações. Com o comparador decrescente, o array sai 99 42 23 15 3 0 -1 -7.


🧭 Navegação

📋 Projetos do curso de C | 📚 Teoria do Capítulo 14 | 🗂️ Hub de projetos