Ponteiro da função Typedef?

459

Estou aprendendo a carregar dinamicamente DLL, mas o que eu não entendo é esta linha

typedef void (*FunctionFunc)();

Eu tenho algumas perguntas. Se alguém puder responder, eu ficaria agradecido.

  1. Por que é typedefusado?
  2. A sintaxe parece estranha; depois voidnão deve haver um nome de função ou algo assim? Parece uma função anônima.
  3. Um ponteiro de função é criado para armazenar o endereço de memória de uma função?

Então, eu estou confuso no momento; você pode esclarecer as coisas para mim?

Jack Harvin
fonte
5
Dê uma olhada no link (última secção) learncpp.com/cpp-tutorial/78-function-pointers
enthusiasticgeek
9
Deve-se notar que, uma vez que o c ++ 11 using FunctionFunc = void (*)();pode ser usado. É um pouco mais claro que você está apenas declarando um nome para um tipo (ponteiro para função)
user362515
apenas para adicionar a @ user362515, forma um pouco mais claro para mim é:using FunctionFunc = void(void);
topspin
@topspin IIRC esses dois não são os mesmos. Um é o tipo de ponteiro de função, o outro é o tipo de função. Há uma conversão implícita, é por isso que funciona, IANA (C ++) L para que alguém possa intervir e me corrigir. De qualquer forma, se a intenção é definir um tipo de ponteiro, acho que a sintaxe com o *é um pouco mais explícita.
User362515 de

Respostas:

463

typedefé uma construção de linguagem que associa um nome a um tipo.
Você o usa da mesma maneira que usaria o tipo original, por exemplo

  typedef int myinteger;
  typedef char *mystring;
  typedef void (*myfunc)();

usando-os como

  myinteger i;   // is equivalent to    int i;
  mystring s;    // is the same as      char *s;
  myfunc f;      // compile equally as  void (*f)();

Como você pode ver, você pode substituir o nome do tipo de digitação pela sua definição acima.

A dificuldade está no ponteiro para funções sintaxe e legibilidade em C e C ++, e isso typedefpode melhorar a legibilidade de tais declarações. No entanto, a sintaxe é apropriada, pois as funções - diferentemente de outros tipos mais simples - podem ter um valor e parâmetros de retorno, portanto a declaração às vezes longa e complexa de um ponteiro para funcionar.

A legibilidade pode começar a ser realmente complicada com ponteiros para matrizes de funções e alguns outros sabores ainda mais indiretos.

Para responder suas três perguntas

  • Por que o typedef é usado? Facilitar a leitura do código - especialmente para ponteiros para funções ou nomes de estrutura.

  • A sintaxe parece estranha (no ponteiro para a declaração de função) Essa sintaxe não é óbvia para leitura, pelo menos no início. Usar uma typedefdeclaração facilita a leitura

  • Um ponteiro de função é criado para armazenar o endereço de memória de uma função? Sim, um ponteiro de função armazena o endereço de uma função. Isso não tem nada a ver com o typedefconstruto que apenas facilita a escrita / leitura de um programa; o compilador apenas expande a definição de typedef antes de compilar o código real.

Exemplo:

typedef int (*t_somefunc)(int,int);

int product(int u, int v) {
  return u*v;
}

t_somefunc afunc = &product;
...
int x2 = (*afunc)(123, 456); // call product() to calculate 123*456
e2-e4
fonte
6
no último exemplo, 'quadrado' não se refere apenas à mesma coisa, ou seja, ponteiro para a função em vez de usar & square.
precisa saber é
2
Pergunta, no seu primeiro exemplo de typedef você possui o formulário, typedef type aliasmas com ponteiros de função parece haver apenas 2 argumentos typedef type. O alias é padronizado para o nome especificado no argumento type?
Dchhetri
2
@ pranavk: Sim, squaree &square(e, de fato, *squaree **square) todos se referem ao mesmo ponteiro de função.
Jonathan Leffler
6
@ user814628: Não está claro o que você está perguntando. Com typedef int newname, você está transformando newnameum alias para int. Com typedef int (*func)(int), você está transformando funcum alias para int (*)(int)- um ponteiro para funcionar usando um intargumento e retornando um intvalor.
Jonathan Leffler
10
Acho que estou confuso sobre o pedido. Com typedef int (*func)(int), eu entendo que func é um alias, apenas um pouco confuso porque o alias está emaranhado com o tipo. Seguindo typedef int INTcomo exemplo, ficaria mais à vontade se o ponteiro da função typedef fosse de forma typedef int(*function)(int) FUNC_1. Dessa forma, posso ver o tipo e o alias em dois token separado, em vez de serem mesclados em um.
dchhetri
188
  1. typedefé usado para alias tipos; neste caso você está aliasing FunctionFuncpara void(*)().

  2. De fato, a sintaxe parece estranha, veja isto:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
    
  3. Não, isso simplesmente diz ao compilador que o FunctionFunctipo será um ponteiro de função, não define um, assim:

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"
    
Jacob Relkin
fonte
27
typedefse não declarar um novo tipo. você pode ter muitos typedefnomes definidos do mesmo tipo e eles não são distintos (por exemplo, sobrecarga incorreta de funções). há algumas circunstâncias em que, com relação à maneira de usar o nome, um typedefnome definido não é exatamente equivalente ao que é definido, mas vários typedefnomes definidos para o mesmo são equivalentes.
Saúde e hth. - Alf
Ah, entendi agora. Obrigado.
precisa
4
+1 na resposta à pergunta 2 - muito claro. O resto das respostas foram claras também, mas que se destaca para mim :-)
Michael van der Westhuizen
33

Sem a typedefpalavra, em C ++, a declaração declararia uma variável FunctionFuncdo tipo ponteiro para funcionar sem argumentos, retornando void.

Em typedefvez disso, define FunctionFunccomo um nome para esse tipo.

Felicidades e hth. - Alf
fonte
5
Esta é realmente uma boa maneira de pensar sobre isso. Se você ler as outras respostas com atenção, elas realmente não abordam a confusão do OP sobre o entrelaçamento do apelido e do nome. Eu aprendi algo novo lendo este post.
Physicsist
9

Se você pode usar o C ++ 11, convém usar a palavra std::function- usingchave.

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;
Halil Kaskavalci
fonte
1
sem C ++ 11 usingpalavra-chave seria como typedef std::function<void(int, std::string)> FunctionFunc;, apenas no caso de alguém quiser um outro invólucro em torno funções sem C ++ 11
Top-Master
2
#include <stdio.h>
#include <math.h>

/*
To define a new type name with typedef, follow these steps:
1. Write the statement as if a variable of the desired type were being declared.
2. Where the name of the declared variable would normally appear, substitute the new type name.
3. In front of everything, place the keyword typedef.
*/

// typedef a primitive data type
typedef double distance;

// typedef struct 
typedef struct{
    int x;
    int y;
} point;

//typedef an array 
typedef point points[100]; 

points ps = {0}; // ps is an array of 100 point 

// typedef a function
typedef distance (*distanceFun_p)(point,point) ; // TYPE_DEF distanceFun_p TO BE int (*distanceFun_p)(point,point)

// prototype a function     
distance findDistance(point, point);

int main(int argc, char const *argv[])
{
    // delcare a function pointer 
    distanceFun_p func_p;

    // initialize the function pointer with a function address
    func_p = findDistance;

    // initialize two point variables 
    point p1 = {0,0} , p2 = {1,1};

    // call the function through the pointer
    distance d = func_p(p1,p2);

    printf("the distance is %f\n", d );

    return 0;
}

distance findDistance(point p1, point p2)
{
distance xdiff =  p1.x - p2.x;
distance ydiff =  p1.y - p2.y;

return sqrt( (xdiff * xdiff) + (ydiff * ydiff) );
}
Amjad
fonte
1
Em que circunstâncias o '&' é necessário? Por exemplo, 'func_p = & findDistance' e 'func_p = findDistance' funcionam igualmente bem?
Donna
1
Um nome de função é um ponteiro, portanto você não precisa usar 'e' com ele. Em outras palavras, '&' é opcional quando indicadores de função são considerados. No entanto, para o tipo de dados primitivo, você precisa usar o '&' para obter seu endereço.
Amjad 30/10
1
Sempre pareceu estranho que o '&' possa ser opcional. Se um typedef for usado para criar um ponteiro para um primitivo (isto typedef (*double) pé, o '&' é opcional? #
314 Donna
Eu acho que você queria digitar typedef double* ppara definir um ponteiro para um duplo. Se você deseja preencher o pponteiro de uma variável primitiva, precisará usar o '&'. Uma observação: você * <nome do ponteiro> desreferencia um ponteiro. O *é usado no ponteiro da função para desreferenciar o ponteiro (nome da função) e ir para o bloco de memória onde as instruções da função estão localizadas.
Amjad
2

Para o caso geral da sintaxe que você pode olhar para o anexo A da norma ANSI C .

Na forma Backus-Naur a partir daí, você pode ver que typedeftem o tipo storage-class-specifier.

No tipo, declaration-specifiersvocê pode ver que pode misturar muitos tipos de especificadores, cuja ordem não importa.

Por exemplo, é correto dizer:

long typedef long a;

para definir o tipo acomo um alias para long long. Portanto, para entender o typedef no uso exaustivo, é necessário consultar uma forma de backus-naur que defina a sintaxe (existem muitas gramáticas corretas para o ANSI C, não apenas o ISO).

Quando você usa typedef para definir um alias para um tipo de função, é necessário colocar o alias no mesmo local em que você coloca o identificador da função. No seu caso, você define o tipo FunctionFunccomo um alias para que um ponteiro funcione cuja verificação de tipo está desativada na chamada e não retorna nada.

alinsoar
fonte