Wie implementiert man eine Klasse in C? [geschlossen]

139

Angenommen, ich muss C verwenden (keine C ++ - oder objektorientierten Compiler) und ich habe keine dynamische Speicherzuordnung. Welche Techniken kann ich verwenden, um eine Klasse zu implementieren, oder eine gute Annäherung an eine Klasse? Ist es immer eine gute Idee, die "Klasse" in eine separate Datei zu isolieren? Angenommen, wir können den Speicher vorab zuweisen, indem wir eine feste Anzahl von Instanzen annehmen oder sogar den Verweis auf jedes Objekt als Konstante vor der Kompilierungszeit definieren. Sie können gerne Annahmen darüber treffen, welches OOP-Konzept ich implementieren muss (es wird variieren) und die jeweils beste Methode vorschlagen.

Beschränkungen:

  • Ich muss C und kein OOP verwenden, da ich Code für ein eingebettetes System schreibe und der Compiler und die bereits vorhandene Codebasis in C sind.
  • Es gibt keine dynamische Speicherzuweisung, da wir nicht über genügend Speicher verfügen, um davon auszugehen, dass wir nicht ausgehen, wenn wir mit der dynamischen Zuweisung beginnen.
  • Die Compiler, mit denen wir arbeiten, haben keine Probleme mit Funktionszeigern
Ben Gartner
quelle
26
Obligatorische Frage: Müssen Sie objektorientierten Code schreiben? Wenn Sie aus irgendeinem Grund tun, ist das in Ordnung, aber Sie werden einen ziemlich harten Kampf führen. Es ist wahrscheinlich das Beste, wenn Sie vermeiden, objektorientierten Code in C zu schreiben. Es ist sicherlich möglich - siehe die ausgezeichnete Antwort von unwind -, aber es ist nicht gerade "einfach", und wenn Sie an einem eingebetteten System mit begrenztem Speicher arbeiten, ist es dies möglicherweise nicht machbar. Ich kann mich jedoch irren - ich versuche nicht, Sie davon abzubringen, sondern präsentiere nur einige Kontrapunkte, die möglicherweise nicht präsentiert wurden.
Chris Lutz
1
Genau genommen müssen wir nicht. Die Komplexität des Systems hat den Code jedoch nicht mehr wartbar gemacht. Meiner Meinung nach besteht der beste Weg, die Komplexität zu reduzieren, darin, einige OOP-Konzepte zu implementieren. Vielen Dank für alle, die innerhalb von 3 Minuten geantwortet haben. Ihr seid verrückt und schnell!
Ben Gartner
8
Dies ist nur meine bescheidene Meinung, aber OOP macht Code nicht sofort wartbar. Dies erleichtert möglicherweise die Verwaltung, ist jedoch nicht unbedingt wartbarer. Sie können "Namespaces" in C (die Apache Portable Runtime stellt allen globalen Symbolen apr_und GLib ihnen Präfixe vor g_, um einen Namespace zu erstellen) und andere Organisationsfaktoren ohne OOP verwenden. Wenn Sie die App trotzdem umstrukturieren möchten, würde ich in Betracht ziehen, einige Zeit damit zu verbringen, eine wartbarere Verfahrensstruktur zu entwickeln.
Chris Lutz
Dies wurde schon endlos diskutiert - haben Sie sich eine der vorherigen Antworten angesehen?
Larry Watanabe
Diese Quelle, die in einer von mir gelöschten Antwort enthalten war, kann ebenfalls hilfreich sein: planetpdf.com/codecuts/pdfs/ooc.pdf Sie beschreibt einen vollständigen Ansatz für die Durchführung von OO in C.
Ruben Steins

Antworten:

86

Dies hängt von dem genauen "objektorientierten" Funktionsumfang ab, den Sie haben möchten. Wenn Sie Dinge wie Überladung und / oder virtuelle Methoden benötigen, müssen Sie wahrscheinlich Funktionszeiger in Strukturen einfügen:

typedef struct {
  float (*computeArea)(const ShapeClass *shape);
} ShapeClass;

float shape_computeArea(const ShapeClass *shape)
{
  return shape->computeArea(shape);
}

Auf diese Weise können Sie eine Klasse implementieren, indem Sie die Basisklasse "erben" und eine geeignete Funktion implementieren:

typedef struct {
  ShapeClass shape;
  float width, height;
} RectangleClass;

static float rectangle_computeArea(const ShapeClass *shape)
{
  const RectangleClass *rect = (const RectangleClass *) shape;
  return rect->width * rect->height;
}

Dazu müssen Sie natürlich auch einen Konstruktor implementieren, der sicherstellt, dass der Funktionszeiger richtig eingerichtet ist. Normalerweise würden Sie der Instanz dynamisch Speicher zuweisen, aber Sie können den Aufrufer dies auch tun lassen:

void rectangle_new(RectangleClass *rect)
{
  rect->width = rect->height = 0.f;
  rect->shape.computeArea = rectangle_computeArea;
}

Wenn Sie mehrere verschiedene Konstruktoren möchten, müssen Sie die Funktionsnamen "dekorieren". Sie können nicht mehr als eine rectangle_new()Funktion haben:

void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
  rectangle_new(rect);
  rect->width = width;
  rect->height = height;
}

Hier ist ein grundlegendes Beispiel für die Verwendung:

int main(void)
{
  RectangleClass r1;

  rectangle_new_with_lengths(&r1, 4.f, 5.f);
  printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
  return 0;
}

Ich hoffe, das gibt Ihnen zumindest einige Ideen. Ein erfolgreiches und umfangreiches objektorientiertes Framework in C finden Sie in der GObject- Bibliothek von glib .

Beachten Sie auch, dass oben keine explizite "Klasse" modelliert wird. Jedes Objekt verfügt über eigene Methodenzeiger, die etwas flexibler sind als in C ++ üblich. Außerdem kostet es Speicher. Sie könnten davon abweichen, indem Sie die Methodenzeiger in eine classStruktur einfügen und eine Möglichkeit für jede Objektinstanz erfinden, auf eine Klasse zu verweisen.

entspannen
quelle
Ist es normalerweise am besten, Funktionen zu erstellen, die const ShapeClass *oder const void *als Argumente dienen , da Sie nicht versuchen mussten, objektorientiertes C zu schreiben ? Es scheint, dass Letzteres bei der Vererbung etwas besser ist, aber ich kann Argumente in beide Richtungen sehen ...
Chris Lutz
1
@ Chris: Ja, das ist eine schwierige Frage. : | GTK + (das GObject verwendet) verwendet die richtige Klasse, dh RectangleClass *. Dies bedeutet, dass Sie häufig Casts ausführen müssen, aber sie bieten praktische Makros, die Ihnen dabei helfen. Sie können also BASECLASS * p immer nur mit SUBCLASS (p) in SUBCLASS * umwandeln.
Entspannen Sie
1
Mein Compiler schlägt in der zweiten Codezeile fehl: float (*computeArea)(const ShapeClass *shape);Das ShapeClassist ein unbekannter Typ.
DanielSank
@DanielSank, der auf das Fehlen einer für die 'typedef struct' erforderlichen Vorwärtsdeklaration zurückzuführen ist (im angegebenen Beispiel nicht dargestellt). Da die structReferenzen selbst eine Deklaration erfordern, bevor sie definiert werden. Dies wird hier in Lundins Antwort anhand eines Beispiels erläutert . Wenn Sie das Beispiel so ändern, dass es die Weiterleitungsdeklaration enthält, sollte Ihr Problem behoben sein. typedef struct ShapeClass ShapeClass; struct ShapeClass { float (*computeArea)(const ShapeClass *shape); };
S. Whittaker
Was passiert, wenn Rechteck eine Funktion hat, die nicht alle Formen erfüllen? Zum Beispiel get_corners (). Ein Kreis würde dies nicht implementieren, aber ein Rechteck könnte. Wie greifen Sie auf eine Funktion zu, die nicht Teil der übergeordneten Klasse ist, von der Sie geerbt haben?
Otus
24

Ich musste es auch einmal für eine Hausaufgabe machen. Ich folgte diesem Ansatz:

  1. Definieren Sie Ihre Datenelemente in einer Struktur.
  2. Definieren Sie Ihre Funktionselemente, die als erstes Argument einen Zeiger auf Ihre Struktur verwenden.
  3. Tun Sie dies in einem Header & einem c. Header für Strukturdefinition und Funktionsdeklarationen, c für Implementierungen.

Ein einfaches Beispiel wäre dies:

/// Queue.h
struct Queue
{
    /// members
}
typedef struct Queue Queue;

void push(Queue* q, int element);
void pop(Queue* q);
// etc.
/// 
erelender
quelle
Dies ist, was ich in der Vergangenheit getan habe, aber mit dem zusätzlichen Fälschungsbereich, indem Funktionsprototypen nach Bedarf entweder in die .c- oder .h-Datei eingefügt werden (wie in meiner Antwort erwähnt).
Taylor Leese
Ich mag das, die Strukturdeklaration reserviert den gesamten Speicher. Aus irgendeinem Grund habe ich vergessen, dass dies gut funktionieren würde.
Ben Gartner
Ich denke du brauchst typedef struct Queue Queue;da drin einen .
Craig McQueen
3
Oder geben Sie einfach struct {/ * Mitglieder * /} Queue ein.
Brooks Moses
#Craig: Danke für die Erinnerung, aktualisiert.
Erelender
12

Wenn Sie nur eine Klasse möchten, verwenden Sie ein Array von structs als "Objekt" -Daten und übergeben Sie Zeiger auf diese an die "Member" -Funktionen. Sie können typedef struct _whatever Whatevervor dem Deklarieren verwenden struct _whatever, um die Implementierung vor dem Clientcode auszublenden. Es gibt keinen Unterschied zwischen einem solchen "Objekt" und dem C-Standardbibliotheksobjekt FILE.

Wenn Sie mehr als eine Klasse mit Vererbung und virtuellen Funktionen möchten, haben Sie häufig Zeiger auf die Funktionen als Mitglieder der Struktur oder einen gemeinsamen Zeiger auf eine Tabelle mit virtuellen Funktionen. Die GObject- Bibliothek verwendet sowohl diesen als auch den typedef-Trick und ist weit verbreitet.

Es gibt auch ein Buch über Techniken für diese online verfügbar - Objektorientierte Programmierung mit ANSI C .

Pete Kirkham
quelle
1
Cool! Irgendwelche anderen Empfehlungen für Bücher über OOP in C? Oder andere moderne Designtechniken in C? (oder eingebettete Systeme?)
Ben Gartner
7

Sie können sich GOBject ansehen. Es ist eine Betriebssystembibliothek, die Ihnen eine ausführliche Möglichkeit bietet, ein Objekt zu erstellen.

http://library.gnome.org/devel/gobject/stable/

Alex
quelle
1
Sehr interessiert. Kennt jemand die Lizenzierung? Für meine Arbeitszwecke funktioniert das Löschen einer Open Source-Bibliothek in einem Projekt aus rechtlicher Sicht wahrscheinlich nicht.
Ben Gartner
GTK + und alle Bibliotheken, die Teil dieses Projekts sind (einschließlich GObject), sind unter der GNU LGPL lizenziert, dh Sie können von proprietärer Software aus auf sie verlinken. Ich weiß jedoch nicht, ob dies für eingebettete Arbeiten machbar sein wird.
Chris Lutz
7

C Schnittstellen und Implementierungen: Techniken zum Erstellen wiederverwendbarer Software , David R. Hanson

http://www.informit.com/store/product.aspx?isbn=0201498413

Dieses Buch behandelt Ihre Frage hervorragend. Es ist in der Addison Wesley Professional Computing-Reihe.

Das grundlegende Paradigma ist ungefähr so:

/* for data structure foo */

FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);
Mark Harrison
quelle
5

Ich werde ein einfaches Beispiel dafür geben, wie OOP in C durchgeführt werden sollte. Mir ist klar, dass dieser Thead aus dem Jahr 2009 stammt, möchte dies aber trotzdem hinzufügen.

/// Object.h
typedef struct Object {
    uuid_t uuid;
} Object;

int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);

/// Person.h
typedef struct Person {
    Object obj;
    char *name;
} Person;

int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);

/// Object.c
#include "object.h"

int Object_init(Object *self)
{
    self->uuid = uuid_new();

    return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
    return self->uuid;
}
int Object_clean(Object *self)
{
    uuid_free(self->uuid);

    return 0;
}

/// Person.c
#include "person.h"

int Person_init(Person *self, char *name)
{
    Object_init(&self->obj); // Or just Object_init(&self);
    self->name = strdup(name);

    return 0;
}
int Person_greet(Person *self)
{
    printf("Hello, %s", self->name);

    return 0;
}
int Person_clean(Person *self)
{
    free(self->name);
    Object_clean(self);

    return 0;
}

/// main.c
int main(void)
{
    Person p;

    Person_init(&p, "John");
    Person_greet(&p);
    Object_get_uuid(&p); // Inherited function
    Person_clean(&p);

    return 0;
}

Das Grundkonzept besteht darin, die 'geerbte Klasse' oben in der Struktur zu platzieren. Auf diese Weise greift der Zugriff auf die ersten 4 Bytes in der Struktur auch auf die ersten 4 Bytes in der 'geerbten Klasse' zu (unter der Annahme nicht verrückter Optimalisierungen). Wenn nun der Zeiger der Struktur auf die 'geerbte Klasse' umgewandelt wird, kann die 'geerbte Klasse' auf die 'geerbten Werte' genauso zugreifen, wie sie normalerweise auf ihre Mitglieder zugreifen würde.

Diese und einige Namenskonventionen für Konstruktoren, Destruktoren, Zuordnungs- und Deallocarion-Funktionen (ich empfehle init, clean, new, free) bringen Sie weit.

Verwenden Sie für virtuelle Funktionen Funktionszeiger in der Struktur, möglicherweise mit Class_func (...). Wrapper auch. Fügen Sie für (einfache) Vorlagen einen size_t-Parameter hinzu, um die Größe zu bestimmen, benötigen Sie einen void * -Zeiger oder einen 'class'-Typ mit genau der Funktionalität, die Sie interessiert. (zB int GetUUID (Object * self); GetUUID (& p);)

yyny
quelle
Haftungsausschluss: Der gesamte Code wurde auf das Smartphone geschrieben. Fügen Sie bei Bedarf Fehlerprüfungen hinzu. Suchen Sie nach Fehlern.
yyny
4

Verwenden Sie a struct, um die Datenelemente einer Klasse zu simulieren. In Bezug auf den Methodenumfang können Sie private Methoden simulieren, indem Sie die Prototypen für private Funktionen in die .c-Datei und die öffentlichen Funktionen in die .h-Datei einfügen.

Taylor Leese
quelle
4
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <uchar.h>

/**
 * Define Shape class
 */
typedef struct Shape Shape;
struct Shape {
    /**
     * Variables header...
     */
    double width, height;

    /**
     * Functions header...
     */
    double (*area)(Shape *shape);
};

/**
 * Functions
 */
double calc(Shape *shape) {
        return shape->width * shape->height;
}

/**
 * Constructor
 */
Shape _Shape() {
    Shape s;

    s.width = 1;
    s.height = 1;

    s.area = calc;

    return s;
}

/********************************************/

int main() {
    Shape s1 = _Shape();
    s1.width = 5.35;
    s1.height = 12.5462;

    printf("Hello World\n\n");

    printf("User.width = %f\n", s1.width);
    printf("User.height = %f\n", s1.height);
    printf("User.area = %f\n\n", s1.area(&s1));

    printf("Made with \xe2\x99\xa5 \n");

    return 0;
};
Pierozi
quelle
3

In Ihrem Fall könnte die gute Annäherung der Klasse eine ADT sein . Aber es wird immer noch nicht dasselbe sein.

Artem Barger
quelle
1
Kann jemand einen kurzen Unterschied zwischen einem abstrakten Datentyp und einer Klasse geben? Ich habe immer von den beiden Konzepten als eng miteinander verbunden.
Ben Gartner
Sie sind in der Tat eng miteinander verbunden. Eine Klasse kann als Implementierung eines ADT angesehen werden, da sie (angeblich) durch eine andere Implementierung ersetzt werden könnte, die dieselbe Schnittstelle erfüllt. Ich denke, es ist schwierig, einen genauen Unterschied anzugeben, da die Konzepte nicht klar definiert sind.
Jørgen Fogh
3

Meine Strategie ist:

  • Definieren Sie den gesamten Code für die Klasse in einer separaten Datei
  • Definieren Sie alle Schnittstellen für die Klasse in einer separaten Header-Datei
  • Alle Mitgliedsfunktionen verwenden ein "ClassHandle", das für den Instanznamen steht (anstelle von o.foo () rufen Sie foo (oHandle) auf.
  • Der Konstruktor wird abhängig von der Speicherzuweisungsstrategie durch eine Funktion void ClassInit (ClassHandle h, int x, int y, ...) ODER ClassHandle ClassInit (int x, int y, ...) ersetzt
  • Alle Mitgliedsvariablen werden als Mitglied einer statischen Struktur in der Klassendatei gespeichert, kapseln sie in die Datei und verhindern, dass externe Dateien darauf zugreifen
  • Die Objekte werden in einem Array der obigen statischen Struktur mit vordefinierten Handles (in der Benutzeroberfläche sichtbar) oder einer festen Anzahl von Objekten gespeichert, die instanziiert werden können
  • Falls nützlich, kann die Klasse öffentliche Funktionen enthalten, die das Array durchlaufen und die Funktionen aller instanziierten Objekte aufrufen (RunAll () ruft jeden Run auf (oHandle)
  • Eine Deinit-Funktion (ClassHandle h) gibt den zugewiesenen Speicher (Array-Index) in der dynamischen Zuordnungsstrategie frei

Hat jemand Probleme, Lücken, potenzielle Fallstricke oder versteckte Vor- und Nachteile bei beiden Variationen dieses Ansatzes? Wenn ich eine Entwurfsmethode neu erfinde (und ich nehme an, dass ich es sein muss), können Sie mich auf den Namen verweisen?

Ben Gartner
quelle
Wenn Sie Ihrer Frage Informationen hinzufügen möchten, sollten Sie Ihre Frage aus Stilgründen so bearbeiten, dass sie diese Informationen enthält.
Chris Lutz
Sie scheinen von der dynamischen Zuweisung von malloc von einem großen Heap zu ClassInit () übergegangen zu sein und dynamisch aus einem Pool fester Größe auszuwählen, anstatt tatsächlich etwas dagegen zu unternehmen, was passiert, wenn Sie nach einem anderen Objekt fragen und nicht über die Ressourcen verfügen, um eines bereitzustellen .
Pete Kirkham
Ja, die Speicherverwaltungslast wird auf den Code verschoben, der ClassInit () aufruft, um zu überprüfen, ob das zurückgegebene Handle gültig ist. Im Wesentlichen haben wir unseren eigenen dedizierten Heap für die Klasse erstellt. Ich bin mir nicht sicher, ob ich einen Weg finden kann, dies zu vermeiden, wenn wir eine dynamische Zuordnung vornehmen möchten, es sei denn, wir haben einen Allzweck-Heap implementiert. Ich würde es vorziehen, das im Heap geerbte Risiko auf eine Klasse zu beschränken.
Ben Gartner
3

Siehe auch diese Antwort und diese

Es ist möglich. Es scheint immer eine gute Idee zu sein, aber danach wird es zu einem Alptraum für die Wartung. Ihr Code ist übersät mit Codeteilen, die alles zusammenbinden. Ein neuer Programmierer hat viele Probleme beim Lesen und Verstehen des Codes, wenn Sie Funktionszeiger verwenden, da nicht klar ist, welche Funktionen aufgerufen werden.

Das Ausblenden von Daten mit get / set-Funktionen ist in C einfach zu implementieren, hört aber dort auf. Ich habe mehrere Versuche in der eingebetteten Umgebung gesehen und am Ende ist es immer ein Wartungsproblem.

Da Sie alle bereit sind, Wartungsprobleme zu haben, würde ich klar steuern.

Gerhard
quelle
2

Mein Ansatz wäre es, die structund alle primär zugeordneten Funktionen in eine separate Quelldatei (en) zu verschieben, damit sie "portabel" verwendet werden können.

Abhängig von Ihrem Compiler können Sie möglicherweise Funktionen in die aufnehmen struct, aber das ist eine sehr compilerspezifische Erweiterung und hat nichts mit der letzten Version des Standards zu tun, den ich routinemäßig verwendet habe :)

Labyrinth
quelle
2
Funktionszeiger sind alle gut. Wir neigen dazu, sie zu verwenden, um große switch-Anweisungen durch eine Nachschlagetabelle zu ersetzen.
Ben Gartner
2

Der erste C ++ - Compiler war tatsächlich ein Präprozessor, der den C ++ - Code in C übersetzte.

Es ist also sehr gut möglich, Klassen in C zu haben. Sie könnten versuchen, einen alten C ++ - Präprozessor auszugraben und zu sehen, welche Art von Lösungen er erstellt.

Kröte
quelle
Das wäre cfront; Beim Hinzufügen von Ausnahmen zu C ++ traten Probleme auf - die Behandlung von Ausnahmen ist nicht trivial.
Jonathan Leffler
2

GTK basiert vollständig auf C und verwendet viele OOP-Konzepte. Ich habe den Quellcode von GTK gelesen und er ist ziemlich beeindruckend und definitiv einfacher zu lesen. Das Grundkonzept ist, dass jede "Klasse" einfach eine Struktur und zugehörige statische Funktionen ist. Die statischen Funktionen akzeptieren alle die "Instanz" -Struktur als Parameter, tun alles, was dann benötigt wird, und geben bei Bedarf Ergebnisse zurück. Beispielsweise können Sie eine Funktion "GetPosition (CircleStruct obj)" haben. Die Funktion würde einfach durch die Struktur graben, die Positionsnummern extrahieren, wahrscheinlich ein neues PositionStruct-Objekt erstellen, das x und y in das neue PositionStruct stecken und es zurückgeben. GTK implementiert die Vererbung sogar auf diese Weise, indem Strukturen in Strukturen eingebettet werden. ziemlich clever.

Raketen sind schnell
quelle
1

Möchten Sie virtuelle Methoden?

Wenn nicht, definieren Sie einfach eine Reihe von Funktionszeigern in der Struktur selbst. Wenn Sie alle Funktionszeiger Standard-C-Funktionen zuweisen, können Sie Funktionen von C in einer sehr ähnlichen Syntax wie unter C ++ aufrufen.

Wenn Sie virtuelle Methoden haben möchten, wird es komplizierter. Grundsätzlich müssen Sie jeder Struktur eine eigene VTable implementieren und der VTable Funktionszeiger zuweisen, je nachdem, welche Funktion aufgerufen wird. Sie benötigen dann eine Reihe von Funktionszeigern in der Struktur selbst, die wiederum den Funktionszeiger in der VTable aufrufen. Dies ist im Wesentlichen das, was C ++ tut.

TBH aber ... wenn Sie Letzteres wollen, ist es wahrscheinlich besser, nur einen C ++ - Compiler zu finden, den Sie verwenden können, und das Projekt neu zu kompilieren. Ich habe nie verstanden, dass C ++ in Embedded nicht verwendbar ist. Ich habe es schon oft benutzt und es funktioniert schnell und hat keine Speicherprobleme. Sicher, Sie müssen etwas vorsichtiger sein, was Sie tun, aber es ist wirklich nicht so kompliziert.

Goz
quelle
Ich habe es bereits gesagt und werde es noch einmal sagen, aber ich werde es noch einmal sagen: Sie benötigen keine Funktionszeiger oder die Fähigkeit, Funktionen aus dem C ++ - Stil von Strukturen aufzurufen, um OOP in C zu erstellen. Bei OOP geht es hauptsächlich um die Vererbung von Funktionen und Variablen (Inhalt) Beides kann in C ohne Funktionszeiger oder Duplizieren von Code erreicht werden.
yyny
0

C ist keine OOP-Sprache, wie Sie zu Recht betonen. Es gibt also keine integrierte Möglichkeit, eine echte Klasse zu schreiben. Am besten schauen Sie sich Strukturen und Funktionszeiger an . Mit diesen können Sie eine Annäherung an eine Klasse erstellen. Da C jedoch prozedural ist, sollten Sie in Betracht ziehen, mehr C-ähnlichen Code zu schreiben (dh ohne zu versuchen, Klassen zu verwenden).

Wenn Sie C verwenden können, können Sie wahrscheinlich auch C ++ verwenden und Klassen abrufen.

Benjamin
quelle
4
Ich werde nicht abstimmen, aber zu Ihrer Information, Funktionszeiger oder die Fähigkeit, Funktionen aus Strukturen aufzurufen (was vermutlich Ihre Absicht ist), hat nichts mit OOP zu tun. Bei OOP geht es hauptsächlich um die Vererbung von Funktionen und Variablen, die beide in C ohne Funktionszeiger oder Duplikationen erreicht werden können.
yyny