Ponteiros para funções
Em nossos programas, não são apenas as variáveis que ocupam espaços na memória: as funções também. Assim, outra possível aplicação de ponteiros consiste na criação de ponteiros para funções.
🧑💻 O código
A declaração de ponteiros para funções segue um formato um pouco diferente do que estávamos acostumados. Além de declarar o tipo de retorno da função ou procedimento (void
, nesse caso), precisamos também informar o tipo de dado de cada parâmetro, caso exista.
Acompanhe:
tipo (*nomeDoPonteiro)(tipoDoParametro);
nomeDoPonteiro = nomeDaFuncao; // sem parênteses
Para facilitar a compreensão há, a seguir, alguns exemplos:
👀 Casos de exemplo
1️⃣ Procedimento sem parâmetro
Para declararmos um ponteiro para um procedimento que não recebe parâmetros, o processo é bem simples: atribuímos um nome ao ponteiro e, em seguida, inserimos ()
(parênteses vazios) para indicar que aquele procedimento não recebe parâmetro nenhum.
Execute o código abaixo e veja que o procedimento procedimentoSemParametro
será executado normalmente, como se tivesse sido chamado da forma tradicional.
#include <stdio.h>
void procedimentoSemParametro(){
printf("Procedimento sem parametro.\n");
}
int main()
{
void (*proc1)();
proc1 = procedimentoSemParametro;
proc1();
}
2️⃣ Procedimento com um parâmetro
Quando o procedimento recebe um parâmetro, precisamos simplesmente incluir o indicador do tipo de dado.
#include <stdio.h>
void procedimentoComUmParametro(int num){
printf("Procedimento com um parametro: %d.\n", num);
}
int main()
{
void (*proc2)(int);
proc2 = procedimentoComUmParametro;
proc2(2);
}
3️⃣ Procedimento com dois parâmetros
No caso de recebimento de mais de um parâmetro, seus tipos devem ser declarados junto ao ponteiro.
#include <stdio.h>
void procedimentoComDoisParametros(int num1, int num2){
printf("Procedimento com dois parametros: %d e %d.\n", num1, num2);
}
int main()
{
void (*proc3)(int, int);
proc3 = procedimentoComDoisParametros;
proc3(7,14);
}
4️⃣ Função com um parâmetro
Na declaração do ponteiro para uma função (que retorna valor), substituímos void
pelo tipo de dado retornado. No caso do código abaixo, int
.
#include <stdio.h>
int funcaoComUmParametro(int num){
printf("Funcao com um parametro: %d.\n", num);
return num * 2;
}
int main()
{
int (*proc4)(int);
proc4 = funcaoComUmParametro;
int ret = proc4(7);
printf("Retornou: %d\n", ret);
}
5️⃣ Passando uma função como parâmetro
Uma das vantagens que alcançamos quando utilizamos ponteiros para funções está na possibilidade de passar uma função para outra. Observe a seguir qual seria a prática padrão:
#include <stdio.h>
void ehPar(int n){
if(n%2==0) printf("%d eh par\n", n);
else printf("%d eh impar\n", n);
}
void receberFuncao(void *pont){
void (*)(int) pont(2);
}
int main()
{
void (*func)(int);
func = ehPar;
receberFuncao(func);
// receberFuncao(ehPar); também funciona passando diretamente a função
return 0;
}
Quando passamos o ponteiro para a função, perdemos a referência de que se trata de um ponteiro para função. Logo, precisamos fazer uma conversão de tipos, indicando que aquele ponteiro contém o endereço de memória de uma função.
Uma possibilidade é indicar, na assinatura da função, que o parâmetro esperado é um ponteiro para função:
void receberFuncao(void (*pont)(int)){
pont(2);
}
Ou, alternativamente, efetuar a conversão de tipos dentro da função:
void receberFuncao(void *pont) {
((void (*)(int))pont)(2);
}
O resultado final, então, ficaria da seguinte forma:
#include <stdio.h>
void ehPar(int n){
if(n%2==0) printf("%d eh par\n", n);
else printf("%d eh impar\n", n);
}
void receberFuncao(void (*pont)(int)){
pont(2);
}
int main()
{
void (*func)(int);
func = ehPar;
receberFuncao(func);
// receberFuncao(ehPar); também funciona passando diretamente a função
return 0;
}
🤹♂️ Aplicações e benefícios
Embora o uso de ponteiros para funções para confuso e desnecessário, ele tem algumas vantagens:
- Flexibilidade: Ponteiros para funções permitem que você altere dinamicamente a função que será chamada em tempo de execução, proporcionando flexibilidade no design do programa.
- CallBacks: Facilita a implementação de callbacks, onde funções são passadas como argumentos para outras funções, permitindo a execução de código personalizado em eventos específicos.
📖 Bibliografia
Livros
- Deitel, P., & Deitel, H. (2022). C how to program: With case studies in applications and SystemsProgramming, global edition (9th ed.). Pearson Education.