📚 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.
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 |
double (*)(double, double)).if/else ou switch por uma simples indexação de array.qsort() da biblioteca padrão com uma função de comparação customizada, o exemplo canônico de “passar comportamento como parâmetro” em C.*(ptr + i) e entendendo que isso é equivalente a ptr[i].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:
soma, subtrai, multiplica, divide), todas com a assinatura double f(double, double).if/switch.divide, sem travar o programa.qsort() ordenando um array de inteiros fixo, usando uma função de comparação escrita por você.*(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
O mesmo do Miniprojeto 01: gcc 8 ou mais novo. No Windows, rode chcp 65001 se os emojis aparecerem quebrados.
🎯 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!
====================================================
🎯 Objetivo: trocar uma cadeia de if/else por indexação de um array de funções.
⌨️ Faça:
divide, se b == 0.0, imprima ` [AVISO] Divisao por zero evitada. Retornando 0.0 e retorne 0.0`.main, declare double (*tabela_operacoes[4])(double, double) = {soma, subtrai, multiplica, divide}; e o array paralelo const char *nomes_operacoes[4].{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 doisdoublee devolvemdouble.
✅ 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.
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.
🎯 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.
🎯 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:
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.
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.
📋 Projetos do curso de C | 📚 Teoria do Capítulo 14 | 🗂️ Hub de projetos