Como foi visto no Capítulo 1, um programa é um conjunto de instruções armazenado na memória, assim como seus dados. Por este motivo, é possível referenciar o endereço de uma função. Em C, o endereço de uma função é acessível ao programador através de uma variável do tipo ponteiro para função.
Ponteiros para funções podem ser passados como argumentos para outras funções, e a função apontada pode ser invocada a partir de seu ponteiro. Um exemplo prático desta capacidade é seu uso em uma rotina de ordenação de elementos de um arranjo. Se o arranjo é de inteiros, então uma função de comparação de inteiros deverá ser suportada, tal como
/* * compara dois inteiros, retornando: * 0 se os dois elementos forem iguais * um inteiro negativo se o primeiro elemento for menor * um inteiro positivo se o primeiro elemento for maior */ int comp_int(int *e1, int *e2) { return(*e1 - *e2); }
O problema surge quando se deseja usar o mesmo algoritmo de ordenação para ordenar outros arranjos de tipos que não sejam inteiros. Por exemplo, se os elementos a comparar forem strings, então a rotina de comparação acima não mais serviria, apesar de todo o restante do algoritmo de ordenação ainda ser basicamente o mesmo.
A solução é passar qual função deve ser usada para a comparação como um dos argumentos para a rotina de ordenação genérica. Esta abordagem é adotada por rotinas usualmente supridas juntamente com o compilador C, tal como qsort para ordenação de arranjos e bsearch para a realização de busca binária em arranjos ordenados.
A forma de declarar uma variável do tipo ponteiro para função é ilustrada no seguinte exemplo, com uma referência à função comp_int definida acima:
main() { /* prototipo de comp_int: */ int comp_int(int *, int *); /* ponteiro para uma funcao retornando inteiro */ int (*apcmp)(); int a, b; apcmp = comp_int; /* inicializa ponteiro */ ... (*apcmp)(a, b); /* invoca funcao */ }
Algumas observações relativas a este exemplo são importantes. A primeira
refere-se à declaração do ponteiro. A declaração de um ponteiro para a
função deve incluir os parênteses em torno do nome da variável ponteiro.
Uma definição na forma int *apcmp();
seria interpretada como o
protótipo de uma função retornando um ponteiro para um inteiro, o que não é
o desejado neste caso.
A segunda observação refere-se à forma utilizada para definir o valor do ponteiro no comando de atribuição. Como o protótipo de comp_int já havia sido definido, então o compilador sabe que este identificador refere-se a uma função. Quando o identificador comp_int é encontrado novamente, desta vez sem parênteses, ele é identificado como o endereço desta função, podendo assim ser atribuído a um ponteiro para uma função com o mesmo tipo de retorno. Repare a semelhança com referências a nomes de arranjos.
Finalmente, a invocação da função através de seu ponteiro: a forma usando o
operador de dereferência (*apcmp)
indica o conteúdo do ponteiro
apcmp, que é a função (neste caso, comp_int). Assim, a
última linha no exemplo é apenas uma invocação para a rotina apontada por
apcmp, e o que vem a seguir de (*apcmp)
são simplesmente os
argumentos para a função. O padrão ANSI também permite que a forma
equivalente,
apcmp(a, b);seja utilizada. Muitos programadores preferem a forma apresentada no exemplo original para tornar claro que um ponteiro para função está sendo usado, embora internamente não haja diferenças entre a ativação de uma função por seu nome ou através de um ponteiro.
Ponteiros para funções tornam-se interessantes quando o programador não pode determinar qual função deve ser executada em uma dada situação a não ser durante a execução do programa. Em tais casos, o trecho do programa referenciando esta ``função variável'' pode ser escrito em termos de ativação de uma função através de ponteiros para funções, os quais são corretamente inicializados em tempo de execução.