Como você passa uma função como parâmetro em C?

616

Eu quero criar uma função que executa uma função passada por parâmetro em um conjunto de dados. Como você passa uma função como parâmetro em C?

andrewrk
fonte
12
Se você estiver usando funções / matrizes como variáveis, SEMPRE use typedef.
Mooing Duck
3
Chamamos-lhe ponteiro Função
AminM
O nome da função deve ser um nome para a função. A maioria das pessoas aprendendo C cobre qsort mais cedo ou mais tarde, o que faz exatamente isso?
Mckenzm
5
@MooingDuck Não concordo com sua sugestão, e sua sugestão não tem fundamentação para apoiar a conclusão. Pessoalmente, prefiro nunca usar o typedef em ponteiros de função e acho que torna o código mais claro e fácil de ler.
andrewrk
2
@andrewrk: Você prefere void funcA(void(*funcB)(int))e void (*funcA())()para typedef void funcB(); void funcA(funcB)e funcB funcA()? Eu não vejo o lado positivo.
Mooing Duck

Respostas:

747

Declaração

Um protótipo para uma função que usa um parâmetro de função é semelhante ao seguinte:

void func ( void (*f)(int) );

Isso indica que o parâmetro fserá um ponteiro para uma função que possui um voidtipo de retorno e que aceita um único intparâmetro. A seguinte função ( print) é um exemplo de uma função que pode ser transmitida funccomo parâmetro porque é do tipo apropriado:

void print ( int x ) {
  printf("%d\n", x);
}

Chamada de Função

Ao chamar uma função com um parâmetro de função, o valor passado deve ser um ponteiro para uma função. Use o nome da função (sem parênteses) para isso:

func(print);

chamaria func, passando a função de impressão para ele.

Função Corpo

Como em qualquer parâmetro, funcagora pode usar o nome do parâmetro no corpo da função para acessar o valor do parâmetro. Digamos que funcaplicará a função que é passada aos números 0-4. Considere, primeiro, como o loop chamaria print diretamente:

for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
  print(ctr);
}

Como funca declaração de parâmetro diz que esse fé o nome de um ponteiro para a função desejada, lembramos primeiro que se fé um ponteiro, então *fé o que faponta (por exemplo, a função printneste caso). Como resultado, basta substituir todas as ocorrências de impressão no loop acima por *f:

void func ( void (*f)(int) ) {
  for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
    (*f)(ctr);
  }
}

Fonte

Niyaz
fonte
40
Nos seus primeiro e último exemplos de código, o * não é obrigatório. Tanto a definição do parâmetro de função quanto a fchamada de função podem ser executadas fexatamente como estão sem *. Pode ser uma boa idéia fazê-lo como você faz, para tornar óbvio que o parâmetro f é um ponteiro de função. Mas isso prejudica a legibilidade com bastante frequência.
Gauthier
5
Veja [c99, 6.9.1§14] para exemplos. Ambos estão corretos, é claro, eu só queria mencionar a alternativa.
Gauthier
6
Mesmo? A resposta com a melhor classificação não faz uma única referência ao uso typedefde ponteiros para função? Desculpe, tem que votar em baixa.
Jonathon Reinhart
3
@ JonathonReinhart, quais seriam as vantagens com a abordagem 'typedef'? Esta versão parece muito mais limpa e também não possui declarações extras. praticamente iniciante aqui.
Abhinav Gauniyal
3
@JonathonReinhart bem visto; deve-se notar explicitamente que typedefs de ponteiro ofuscam o código e, portanto, não devem ser usados.
MM
128

Essa pergunta já tem a resposta para definir ponteiros de função, no entanto, eles podem ficar muito confusos, especialmente se você os estiver passando pelo aplicativo. Para evitar esse desagrado, recomendo que você digite o ponteiro da função em algo mais legível. Por exemplo.

typedef void (*functiontype)();

Declara uma função que retorna nula e não aceita argumentos. Para criar um ponteiro de função para esse tipo, agora você pode fazer:

void dosomething() { }

functiontype func = &dosomething;
func();

Para uma função que retorna um int e aceita um char, você faria

typedef int (*functiontype2)(char);

e usá-lo

int dosomethingwithchar(char a) { return 1; }

functiontype2 func2 = &dosomethingwithchar
int result = func2('a');

Existem bibliotecas que podem ajudar a transformar ponteiros de função em bons tipos legíveis. A biblioteca de funções boost é excelente e vale a pena!

boost::function<int (char a)> functiontype2;

é muito melhor do que o acima.

roo
fonte
4
Se você deseja "transformar um ponteiro de função em um tipo", não precisa da biblioteca de reforço. Apenas use typedef; é mais simples e não requer bibliotecas extras.
Wizzwizz4
73

Desde o C ++ 11, você pode usar a biblioteca funcional para fazer isso de maneira sucinta e genérica. A sintaxe é, por exemplo,

std::function<bool (int)>

onde boolé o tipo de retorno aqui de uma função de um argumento cujo primeiro argumento é do tipo int.

Eu incluí um exemplo de programa abaixo:

// g++ test.cpp --std=c++11
#include <functional>

double Combiner(double a, double b, std::function<double (double,double)> func){
  return func(a,b);
}

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

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

int main(){
  Combiner(12,13,Add);
  Combiner(12,13,Mult);
}

Às vezes, porém, é mais conveniente usar uma função de modelo:

// g++ test.cpp --std=c++11

template<class T>
double Combiner(double a, double b, T func){
  return func(a,b);
}

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

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

int main(){
  Combiner(12,13,Add);
  Combiner(12,13,Mult);
}
Richard
fonte
43
A questão é sobre C; C ++ não se aplica aqui.
Super Cat
16
A pergunta para C ++ ( stackoverflow.com/questions/6339970/… ) é referida aqui, então acho que esta resposta está em vigor.
mr_T
8
Talvez a pergunta para C ++ não deve ser referido aqui, porque esta pergunta para é C.
Michael Fulton
5
Essa pergunta surgiu primeiro quando eu procurei "chamada de função c ++ como parâmetro", portanto, essa resposta está cumprindo bem sua finalidade aqui, independentemente.
precisa saber é o seguinte
25

Passe o endereço de uma função como parâmetro para outra função, como mostrado abaixo

#include <stdio.h>

void print();
void execute(void());

int main()
{
    execute(print); // sends address of print
    return 0;
}

void print()
{
    printf("Hello!");
}

void execute(void f()) // receive address of print
{
    f();
}

Também podemos passar a função como parâmetro usando o ponteiro de função

#include <stdio.h>

void print();
void execute(void (*f)());

int main()
{
    execute(&print); // sends address of print
    return 0;
}

void print()
{
    printf("Hello!");
}

void execute(void (*f)()) // receive address of print
{
    f();
}
Yogeesh HT
fonte
1
Resposta muito boa, com blocos inteiros de código (em vez de dividir tudo em uma bagunça incompreensível). Você poderia elaborar as diferenças das duas técnicas?
Rafael Eyng
5

As funções podem ser "passadas" como ponteiros de função, conforme ISO C11 6.7.6.3p8: " Uma declaração de um parâmetro como '' tipo de retorno de função '' deve ser ajustada para '' ponteiro para tipo de retorno de função '' , como em 6.3 .2.1. ". Por exemplo, isto:

void foo(int bar(int, int));

é equivalente a isso:

void foo(int (*bar)(int, int));
doppelheathen
fonte
De que documento você está citando? Algum link para ele?
Rafael Eyng 29/01
1
Estou citando o padrão ISO C11.
doppelheathen
4

Você precisa passar um ponteiro de função . A sintaxe é um pouco complicada, mas é realmente poderosa quando você se familiariza com ela.

saint_groceon
fonte
-2

Não é realmente uma função, mas é um pedaço de código localizado. Claro que não passa o código apenas o resultado. Não funcionará se passado para um distribuidor de eventos para ser executado posteriormente (como o resultado é calculado agora e não quando o evento ocorre). Mas ele localiza seu código em um único local, se é tudo o que você está tentando fazer.

#include <stdio.h>

int IncMultInt(int a, int b)
{
    a++;
    return a * b;
}

int main(int argc, char *argv[])

{
    int a = 5;
    int b = 7;

    printf("%d * %d = %d\n", a, b, IncMultInt(a, b));

    b = 9;

    // Create some local code with it's own local variable
    printf("%d * %d = %d\n", a, b,  ( { int _a = a+1; _a * b; } ) );

    return 0;
}
Walter
fonte
Você está apenas invocando uma função. Como você passaria alguma outra função no lugar de IncMultInt?
Tejas Pendse