C - Funktion innerhalb der Struktur


82

Ich versuche eine Funktion innerhalb einer Struktur zu erstellen, bis jetzt habe ich diesen Code:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno AddClient() 

        {
            /* code */
        }

};

int main()
{

    client_t client;

    //code ..

    client.AddClient();

}

Fehler : client.h: 24: 2: Fehler: erwartet ':', ',', ';', '}' oder ' Attribut ' vor '{' Token.

Welches ist der richtige Weg, um es zu tun?


12
Sie können keine Funktionen in Strukturen in C haben. Sie können jedoch versuchen, dies durch Funktionszeiger grob zu simulieren.
Fingolfin

1
Sind Funktionszeiger ein akzeptabler Ersatz? stackoverflow.com/a/840703/635678
Dan O

Antworten:


104

Dies kann nicht direkt durchgeführt werden, aber Sie können dasselbe mithilfe von Funktionszeigern emulieren und den Parameter "this" explizit übergeben:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno (*AddClient)(client_t *);    
};

pno client_t_AddClient(client_t *self) { /* code */ }

int main()
{

    client_t client;
    client.AddClient = client_t_AddClient; // probably really done in some init fn

    //code ..

    client.AddClient(&client);

}

Es stellt sich jedoch heraus, dass Sie dadurch nicht wirklich viel kaufen. Daher werden nicht viele C-APIs in diesem Stil implementiert, da Sie auch einfach Ihre externe Funktion aufrufen und die Instanz übergeben können.


3
Die X11-API macht so etwas. Siehe XImage .
Hydranix

1
Code, der unter der Windows-API liegt, ist voll davon. Technisch gesehen ist eine "Fensterklasse" eine Struktur mit zwei Rückruffunktionszeigern und offenem Ende (für diese "Fensterklassenspezifischen Daten", die Sie bei der Registrierung angeben können)
Swift - Friday Pie

1
befolgte diese Schritte, aber wenn ich struct.function = newFunction mache, druckt der Compiler: Fehler: erwartet '=', ',', ';', 'asm' oder ' attribute ' before '.' Token
Bonfra

Dies ist auch nützlich, wenn Sie ein gemeinsames Ziel (z. B. Festplatte) haben möchten, das eine Lese- und eine Schreibfunktion hat, aber von einem Festplattentyp bis zum nächsten variiert
Menotdan

Es kann Ihnen viel kosten, wenn die tatsächliche Implementierung zur Laufzeit ausgewählt wird. Wenn Sie nur eine statische Funktion haben, müssen Sie diese Funktion bei jedem Funktionsaufruf immer wieder neu auswählen. Wenn Sie einen Funktionszeiger verwenden, erfolgt die Auswahl nur einmal. Die Auswahl kann sich sogar während der gesamten Lebensdauer der Struktur ändern.
Mecki

23

Wie andere angemerkt haben, ist das Einbetten von Funktionszeigern direkt in Ihre Struktur normalerweise für spezielle Zwecke reserviert, z. B. für eine Rückruffunktion.

Was Sie wahrscheinlich wollen, ist eher eine virtuelle Methodentabelle.

typedef struct client_ops_t client_ops_t;
typedef struct client_t client_t, *pno;

struct client_t {
    /* ... */
    client_ops_t *ops;
};

struct client_ops_t {
    pno (*AddClient)(client_t *);
    pno (*RemoveClient)(client_t *);
};

pno AddClient (client_t *client) { return client->ops->AddClient(client); }
pno RemoveClient (client_t *client) { return client->ops->RemoveClient(client); }

Das Hinzufügen weiterer Operationen ändert nun nichts an der Größe der client_tStruktur. Diese Art der Flexibilität ist nur dann nützlich, wenn Sie viele Arten von Clients definieren müssen oder Benutzern Ihrer client_tBenutzeroberfläche ermöglichen möchten, das Verhalten der Vorgänge zu verbessern.

Diese Art von Struktur erscheint in echtem Code. Die OpenSSL-BIO-Schicht sieht ähnlich aus, und auch UNIX-Gerätetreiberschnittstellen haben eine solche Schicht.


14

Dies funktioniert nur in C ++. Funktionen in Strukturen sind kein Merkmal von C.

Gleiches gilt für Ihren Client.AddClient (); call ... Dies ist ein Aufruf für eine Member-Funktion, bei der es sich um objektorientierte Programmierung handelt, dh C ++.

Konvertieren Sie Ihre Quelle in eine CPP-Datei und stellen Sie sicher, dass Sie entsprechend kompilieren.

Wenn Sie sich an C halten müssen, ist der folgende Code (irgendwie) das Äquivalent:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

};


pno AddClient(pno *pclient) 
{
    /* code */
}


int main()
{

    client_t client;

    //code ..

    AddClient(client);

}

1
Dies ist ein Uni-Projekt und ich muss C verwenden. Gibt es eine Möglichkeit, das zu erreichen, was ich in C will?
xRed

Verschieben Sie diese Funktion einfach außerhalb der Struktur und lassen Sie sie einen Zeiger auf Ihre Instanz akzeptieren.
Benutzer123

Das client.AddClient () -Ding wird ohne ein bisschen komplexe Magie nicht möglich sein (siehe die andere Antwort).
QSQ

12

Wie wäre es damit?

#include <stdio.h>

typedef struct hello {
    int (*someFunction)();
} hello;

int foo() {
    return 0;
}

hello Hello() {
    struct hello aHello;
    aHello.someFunction = &foo;
    return aHello;
}

int main()
{
    struct hello aHello = Hello();
    printf("Print hello: %d\n", aHello.someFunction());

    return 0;
} 

Eine "kleine" JavaScrip-ähnliche Lösung
The Bitman

7

Sie versuchen, Code nach Struktur zu gruppieren. Die C-Gruppierung erfolgt nach Datei. Sie fügen alle Funktionen und internen Variablen in einen Header oder einen Header und eine aus einer AC-Quelldatei kompilierte Objektdatei ".o" ein.

Für ein C-Programm, das keine objektorientierte Sprache ist, ist es nicht erforderlich, die Objektorientierung von Grund auf neu zu erfinden.

Ich habe das schon mal gesehen. Es ist eine seltsame Sache. Einige von ihnen haben eine Abneigung dagegen, ein Objekt, das sie ändern möchten, in eine Funktion zu übergeben, um es zu ändern, obwohl dies die Standardmethode ist.

Ich beschuldige C ++, weil es die Tatsache verborgen hat, dass das Klassenobjekt immer der erste Parameter in einer Mitgliedsfunktion ist, aber es ist versteckt. Es sieht also so aus, als würde das Objekt nicht an die Funktion übergeben, obwohl dies der Fall ist.

Client.addClient(Client& c); // addClient first parameter is actually 
                             // "this", a pointer to the Client object.

C ist flexibel und kann vorübergehende Dinge als Referenz nehmen.

Die AC-Funktion gibt häufig nur ein Statusbyte oder int zurück und wird häufig ignoriert. In Ihrem Fall könnte eine richtige Form sein

 err = addClient( container_t  cnt, client_t c);
 if ( err != 0 )
   { fprintf(stderr, "could not add client (%d) \n", err ); 

addClient wäre in Client.h oder Client.c


Der Ansatz hat Vor- und Nachteile. Nur weil es so war, wie es immer gemacht wurde und sich sowohl auf die Nicht-OO ( do(object, subject)) - als auch auf die OO ( subject.do(object)) -Strategie zum Anordnen von Funktionen bezieht, heißt das nicht, dass wir aufhören sollten, es auszuprobieren. Ich denke , dass es total gültig ist dies darauf hinzuweisen, ist nicht der historische Weg C geschrieben wurde, und dass C nicht braucht diese Art und Weise geschrieben werden ( das gleiche gilt für viele Sprachen, besonders thoes aus prozedurale, funktionalen oder logischen Paradigmen), aber ich nicht, wir müssen das Muster aktiv entmutigen. Es hat auch einige Vorteile
hiljusti
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.