Puntatore alla funzione typedef?


460

Sto imparando come caricare dinamicamente DLL ma quello che non capisco è questa riga

typedef void (*FunctionFunc)();

Ho alcune domande. Se qualcuno è in grado di risponderle, sarei grato.

  1. Perché viene typedefusato?
  2. La sintassi sembra strana; dopo voidnon ci dovrebbe essere un nome di funzione o qualcosa del genere? Sembra una funzione anonima.
  3. È stato creato un puntatore a funzione per memorizzare l'indirizzo di memoria di una funzione?

Quindi sono confuso al momento; puoi chiarire le cose per me?


5
Dai un'occhiata al link (ultima sezione) learncpp.com/cpp-tutorial/78-function-pointers
enthusiasticgeek

9
Va notato che poiché c ++ 11 using FunctionFunc = void (*)();può essere utilizzato invece. È un po 'più chiaro che stai solo dichiarando un nome per un tipo (puntatore alla funzione)
user362515

solo per aggiungere a @ user362515, una forma un po 'più chiara per me è:using FunctionFunc = void(void);
topspin

@topspin IIRC questi due non sono gli stessi. Uno è un tipo di puntatore a funzione, l'altro è un tipo di funzione. C'è una conversione implicita, ecco perché funziona, IANA (C ++) L, quindi si può intervenire e correggermi. In ogni caso, se l'intenzione è quella di definire un tipo di puntatore, penso che la sintassi con il *sia un po 'più esplicita.
user362515

Risposte:


463

typedefè un costrutto linguistico che associa un nome a un tipo.
Lo usi nello stesso modo in cui utilizzeresti il ​​tipo originale, per esempio

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

usandoli come

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

Come puoi vedere, puoi semplicemente sostituire il nome digitato con la sua definizione data sopra.

La difficoltà sta nel puntatore alla sintassi e alla leggibilità delle funzioni in C e C ++ e la typedefpossibilità di migliorare la leggibilità di tali dichiarazioni. Tuttavia, la sintassi è appropriata, poiché le funzioni - a differenza di altri tipi più semplici - possono avere un valore e parametri di ritorno, quindi la dichiarazione a volte lunga e complessa di un puntatore alla funzione.

La leggibilità può iniziare a essere davvero complicata con i puntatori agli array di funzioni e ad alcuni altri sapori ancora più indiretti.

Per rispondere alle tue tre domande

  • Perché viene utilizzato typedef? Per facilitare la lettura del codice, in particolare per i puntatori a funzioni o nomi di strutture.

  • La sintassi sembra strana (nel puntatore alla dichiarazione di funzione) Che la sintassi non è ovvia da leggere, almeno all'inizio. L'uso di una typedefdichiarazione invece facilita la lettura

  • È stato creato un puntatore a funzione per memorizzare l'indirizzo di memoria di una funzione? Sì, un puntatore a funzione memorizza l'indirizzo di una funzione. Questo non ha nulla a che fare con il typedefcostrutto che facilita solo la scrittura / lettura di un programma; il compilatore espande semplicemente la definizione typedef prima di compilare il codice effettivo.

Esempio:

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

6
nell'ultimo esempio, 'quadrato' non si riferirebbe alla stessa cosa, cioè puntatore alla funzione invece di usare & quadrato.
pranavk,

2
Domanda, nel primo esempio typedef si ha della forma typedef type aliasma con puntatori a funzione ci sembra essere solo 2 argomenti, typedef type. L'alias è predefinito sul nome specificato nell'argomento type?
Dchhetri,

2
@pranavk: Sì, squaree &square(e, anzi, *squaree **square) si riferiscono tutti allo stesso puntatore a funzione.
Jonathan Leffler

6
@ user814628: Non è chiaro cosa stai chiedendo. Con typedef int newname, stai diventando newnameun alias per int. Con typedef int (*func)(int), stai trasformando funcin un alias per int (*)(int)- un puntatore a funzione che accetta un intargomento e restituisce un intvalore.
Jonathan Leffler,

10
Immagino di essere solo confuso per l'ordinazione. Con typedef int (*func)(int), capisco che func è un alias, solo un po 'confuso perché l'alias è aggrovigliato con il tipo. Passando typedef int INTad esempio, sarei più semplice se il puntatore alla funzione typedef fosse di forma typedef int(*function)(int) FUNC_1. In questo modo posso vedere il tipo e l'alias in due token separati invece di essere mesh in uno.
Dchhetri,

188
  1. typedefè usato per alias tipi; in questo caso si sta aliasing FunctionFunca void(*)().

  2. In effetti la sintassi sembra strana, date un'occhiata a questo:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
    
  3. No, questo dice semplicemente al compilatore che il FunctionFunctipo sarà un puntatore a funzione, non ne definisce uno, come questo:

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"
    

27
typedefnon senza dichiarare un nuovo tipo. si può avere molti typedefnomi -defined dello stesso tipo, e sono non distinte (ad esempio WRT. sovraccarico di funzioni). ci sono alcune circostanze in cui, rispetto a come puoi usare il nome, un typedefnome definito non è esattamente equivalente a quello che è definito, ma più typedefnomi definiti per lo stesso sono equivalenti.
Saluti e hth. - Alf,

Ah ho capito adesso. Grazie.
Jack Harvin,

4
+1 per la risposta alla domanda 2 - molto chiaro. Anche le altre risposte erano chiare, ma quella si distingue per me :-)
Michael van der Westhuizen,

33

Senza la typedefparola, in C ++ la dichiarazione dichiarerebbe una variabile FunctionFuncdi tipo pointer per funzionare senza argomenti, restituendo void.

Con typedefesso invece si definisce FunctionFunccome un nome per quel tipo.


5
Questo è davvero un buon modo di pensarci. Se leggi attentamente le altre risposte, in realtà non risolvono la confusione del PO sull'entanglement dell'alias e del nome. Ho imparato qualcosa di nuovo leggendo questo post.
Fisico pazzo,

9

Se è possibile utilizzare C ++ 11, è possibile che si desideri utilizzare std::functione la usingparola chiave.

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;

1
senza la usingparola chiave C ++ 11 sarebbe come typedef std::function<void(int, std::string)> FunctionFunc;, nel caso in cui qualcuno volesse un altro wrapper per le funzioni senza 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) );
}

1
In quali circostanze è necessario "&"? Ad esempio, 'func_p = & findDistance' e 'func_p = findDistance' funzionano ugualmente bene?
Donna,

1
Un nome di funzione è un puntatore, quindi non è necessario utilizzare '&' con esso. In altre parole, '&' è facoltativo quando vengono considerati i puntatori a funzione. Tuttavia, per il tipo di dati primitivo devi usare '&' per ottenere il suo indirizzo.
Amjad,

1
È sempre sembrato strano che "&" possa mai essere facoltativo. Se un typedef viene utilizzato per creare un puntatore a una primitiva (ad esempio typedef (*double) p, "&" è facoltativo?
Donna,

Penso che tu abbia voluto digitare typedef double* pper definire un puntatore a doppio. Se vuoi popolare il ppuntatore da una variabile primitiva, dovrai usare '&'. Una nota a margine, noi * <nome puntatore> per dereferenziare un puntatore. La *è utilizzata nel puntatore a funzione per dereference il puntatore (nome della funzione) e andare al blocco di memoria in cui si trovano le istruzioni di funzione.
Amjad,

2

Per caso generale di sintassi si può guardare allegato A dello standard ANSI C .

Nella forma Backus-Naur da lì, puoi vedere che typedefha il tipo storage-class-specifier.

Nel tipo declaration-specifierspuoi vedere che puoi mescolare molti tipi di identificatore, il cui ordine non ha importanza.

Ad esempio, è corretto dire,

long typedef long a;

per definire il tipo acome alias per long long. Quindi, per capire il typedef sull'uso esauriente, è necessario consultare un modulo backus-naur che definisce la sintassi (ci sono molte grammatiche corrette per ANSI C, non solo quella di ISO).

Quando si utilizza typedef per definire un alias per un tipo di funzione, è necessario posizionare l'alias nello stesso punto in cui si inserisce l'identificatore della funzione. Nel tuo caso, definisci il tipo FunctionFunccome alias affinché un puntatore funzioni il cui controllo del tipo è disabilitato alla chiamata e non restituisce nulla.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.