Typedef Funktionszeiger?

458

Ich lerne, wie man DLLs dynamisch lädt, aber was ich nicht verstehe, ist diese Zeile

typedef void (*FunctionFunc)();

Ich habe ein paar Fragen. Wenn jemand in der Lage ist, ihnen zu antworten, wäre ich dankbar.

  1. Warum wird typedefverwendet?
  2. Die Syntax sieht seltsam aus; Nachher voidsollte es keinen Funktionsnamen oder so geben? Es sieht aus wie eine anonyme Funktion.
  3. Wird ein Funktionszeiger zum Speichern der Speicheradresse einer Funktion erstellt?

Also bin ich im Moment verwirrt; Kannst du die Dinge für mich klären?

Jack Harvin
quelle
5
Werfen
enthusiasticgeek
9
Zu beachten ist, dass using FunctionFunc = void (*)();stattdessen c ++ 11 verwendet werden kann. Es ist ein bisschen klarer, dass Sie nur einen Namen für einen Typ deklarieren (Zeiger auf Funktion)
user362515
Nur um @ user362515 hinzuzufügen, ist eine etwas klarere Form für mich:using FunctionFunc = void(void);
Topspin
@ Topspin IIRC diese beiden sind nicht gleich. Einer ist ein Funktionszeigertyp, der andere ist ein Funktionstyp. Es gibt eine implizite Konvertierung, deshalb funktioniert es, IANA (C ++) L, also kann man mich einschalten und korrigieren. In jedem Fall, wenn die Absicht besteht, einen Zeigertyp zu definieren, denke ich, dass die Syntax mit dem *etwas expliziter ist.
user362515

Antworten:

463

typedefist ein Sprachkonstrukt, das einem Typ einen Namen zuordnet.
Sie verwenden es beispielsweise genauso wie den Originaltyp

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

mit ihnen wie

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

Wie Sie sehen können, können Sie den typisierten Namen einfach durch die oben angegebene Definition ersetzen .

Die Schwierigkeit liegt im Zeiger auf Funktionssyntax und Lesbarkeit in C und C ++ und typedefkann die Lesbarkeit solcher Deklarationen verbessern. Die Syntax ist jedoch angemessen, da Funktionen - im Gegensatz zu anderen einfacheren Typen - einen Rückgabewert und Parameter haben können, daher die manchmal langwierige und komplexe Deklaration eines Zeigers auf die Funktion.

Die Lesbarkeit kann mit Zeigern auf Funktionsarrays und einigen anderen, noch indirekteren Varianten sehr schwierig werden.

Um Ihre drei Fragen zu beantworten

  • Warum wird typedef verwendet? Erleichtert das Lesen des Codes - insbesondere bei Zeigern auf Funktionen oder Strukturnamen.

  • Die Syntax sieht seltsam aus (im Zeiger auf die Funktionsdeklaration). Diese Syntax ist zumindest zu Beginn nicht offensichtlich zu lesen. Die Verwendung einer typedefDeklaration erleichtert stattdessen das Lesen

  • Wird ein Funktionszeiger zum Speichern der Speicheradresse einer Funktion erstellt? Ja, ein Funktionszeiger speichert die Adresse einer Funktion. Dies hat nichts mit dem typedefKonstrukt zu tun, das nur das Schreiben / Lesen eines Programms erleichtert; Der Compiler erweitert lediglich die typedef-Definition, bevor der eigentliche Code kompiliert wird.

Beispiel:

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
quelle
6
Im letzten Beispiel würde sich "Quadrat" nicht nur auf dasselbe beziehen, dh auf die Funktion, anstatt & Quadrat zu verwenden.
Pranavk
2
Frage, in Ihrem ersten typedef-Beispiel haben Sie das Formular, typedef type aliasaber mit Funktionszeigern scheint es nur 2 Argumente zu geben typedef type. Ist der Alias ​​standardmäßig auf den im Typargument angegebenen Namen eingestellt?
Dchhetri
2
@pranavk: Ja, squareund &square(und tatsächlich *squareund **square) beziehen sich alle auf denselben Funktionszeiger.
Jonathan Leffler
6
@ user814628: Es ist nicht ganz klar, was Sie fragen. Mit machen typedef int newnameSie newnameeinen Alias ​​für int. Mit machen typedef int (*func)(int)Sie funceinen Alias ​​für int (*)(int)- einen Zeiger auf die Funktion, die ein intArgument nimmt und einen intWert zurückgibt.
Jonathan Leffler
10
Ich glaube, ich bin nur verwirrt über die Bestellung. Mit typedef int (*func)(int)verstehe ich, dass func ein Alias ​​ist, nur ein wenig verwirrt, weil der Alias ​​mit dem Typ verwickelt ist. Als typedef int INTBeispiel wäre es einfacher, wenn der typedef-Funktionszeiger eine Form hätte typedef int(*function)(int) FUNC_1. Auf diese Weise kann ich den Typ und den Alias ​​in zwei separaten Token sehen, anstatt in einem zu vernetzen.
Dchhetri
188
  1. typedefwird für Alias-Typen verwendet; In diesem Fall ist ein Aliasing FunctionFuncfür void(*)().

  2. In der Tat sieht die Syntax seltsam aus. Schauen Sie sich das an:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
    
  3. Nein, dies sagt dem Compiler einfach, dass der FunctionFuncTyp ein Funktionszeiger sein wird, er definiert keinen wie diesen:

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"
    
Jacob Relkin
quelle
27
typedefhat nicht einen neuen Typ deklarieren. Sie können viele typedefdefinierte Namen desselben Typs haben, die sich nicht unterscheiden (z. B. Überladung von Funktionen). Unter bestimmten Umständen typedefentspricht ein definierter Name in Bezug auf die Verwendung des Namens nicht genau dem, als was er definiert ist, sondern mehrere typedefdefinierte Namen für denselben Namen.
Prost und hth. - Alf
Ah, ich verstehe es jetzt. Vielen Dank.
Jack Harvin
4
+1 für die Antwort auf Frage 2 - sehr klar. Der Rest der Antworten war auch klar, aber das fällt mir auf :-)
Michael van der Westhuizen
33

Ohne das typedefWort würde die Deklaration in C ++ eine Variable FunctionFuncvom Typ Zeiger auf die Funktion ohne Argumente deklarieren und zurückgeben void.

Mit dem typedefdefiniert es stattdessen FunctionFuncals Namen für diesen Typ.

Prost und hth. - Alf
quelle
5
Dies ist eine wirklich gute Möglichkeit, darüber nachzudenken. Wenn Sie die anderen Antworten sorgfältig lesen, wird die Verwirrung des OP über die Verflechtung von Alias ​​und Name nicht wirklich behoben. Ich habe durch das Lesen dieses Beitrags etwas Neues gelernt.
Mad Physicist
9

Wenn Sie C ++ 11 verwenden können , die Sie verwenden möchten std::functionund usingSchlüsselwort.

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;
Halil Kaskavalci
quelle
1
ohne C ++ 11 usingSchlüsselwort wäre wie typedef std::function<void(int, std::string)> FunctionFunc;, nur für den Fall, dass jemand einen anderen Wrapper um Funktionen ohne 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
quelle
1
Unter welchen Umständen wird das '&' benötigt? Funktionieren beispielsweise 'func_p = & findDistance' und 'func_p = findDistance' gleich gut?
Donna
1
Ein Funktionsname ist ein Zeiger, daher müssen Sie nicht '&' verwenden. Mit anderen Worten, '&' ist optional, wenn Funktionszeiger berücksichtigt werden. Für den primitiven Datentyp müssen Sie jedoch das '&' verwenden, um die Adresse abzurufen.
Amjad
1
Es ist einfach immer seltsam, dass das '&' jemals optional sein kann. Wenn ein typedef verwendet wird, um einen Zeiger auf ein Grundelement zu erstellen (dh typedef (*double) pist das '&' optional?
Donna
Ich denke, Sie wollten tippen typedef double* p, um einen Zeiger auf ein Double zu definieren. Wenn Sie den pZeiger aus einer primitiven Variablen füllen möchten , müssen Sie das '&' verwenden. Eine Randnotiz, wir Sie * <Pointername>, um einen Zeiger zu dereferenzieren. Das *wird im Funktionszeiger verwendet, um den Zeiger (Funktionsname) zu dereferenzieren und zu dem Speicherblock zu wechseln, in dem sich die Funktionsanweisungen befinden.
Amjad
2

Den allgemeinen Fall der Syntax finden Sie in Anhang A des ANSI C-Standards .

In der Backus-Naur-Form von dort können Sie sehen, dass typedefder Typ hat storage-class-specifier.

Im Typ sehen declaration-specifiersSie, dass Sie viele Spezifizierertypen mischen können, deren Reihenfolge keine Rolle spielt.

Zum Beispiel ist es richtig zu sagen:

long typedef long a;

um den Typ aals Alias ​​für zu definieren long long. Um das typedef über die erschöpfende Verwendung zu verstehen, müssen Sie eine Backus-Naur-Form konsultieren, die die Syntax definiert (es gibt viele korrekte Grammatiken für ANSI C, nicht nur die von ISO).

Wenn Sie mit typedef einen Alias ​​für einen Funktionstyp definieren, müssen Sie den Alias ​​an derselben Stelle platzieren, an der Sie die Kennung der Funktion platziert haben. In Ihrem Fall definieren Sie den Typ FunctionFuncals Alias ​​für einen Zeiger auf eine Funktion, deren Typprüfung beim Aufruf deaktiviert ist und nichts zurückgibt.

Alinsoar
quelle