Kann ich eine Struktur in C “erweitern”?

Lesezeit: 6 Minuten

Benutzer-Avatar
chacham15

typedef struct foo_s {
    int a;
} foo;

typedef struct bar_s {
    foo;
    int b;
} bar;

Im Wesentlichen möchte ich tun:

bar b;
b.a;

Ich weiß, dass ich b.foo_name.a tun könnte, wenn ich die foo-Struktur in bar benannt hätte, aber ich würde es vorziehen, dies nicht zu tun.

Irgendeine Möglichkeit, dies zu tun?

Diese Frage hat eine Vielzahl unterschiedlicher Antworten erhalten, also lassen Sie mich die Notwendigkeit erklären. Der Grund, warum ich dies tun möchte, ist, dass ich eine Bibliothek habe, die ich an meine Situation anpassen muss, was bedeutet, dass ich die ursprüngliche Struct-Deklaration nicht ändern kann. Außerdem muss ich nur 1 Element am Anfang der Struktur hinzufügen (warum am Anfang?, weil ich eine ‘Objekt’-Struktur habe, die alle Strukturen im Projekt anführt). Ich könnte die Struktur einfach einbetten, wie Sie es erwähnen, aber es ist WIRKLICH ärgerlich, da alle Referenzen in „variable->image.location“ dieses „image“ eingegeben werden müssen. Eine Milliarde Typen getippt ist wirklich nervig.

  • Einige Compiler bieten genau dieses Verhalten als Erweiterung an. Ich persönlich denke, es birgt zu viel Risiko (Nesting structs sechs Schichten tief und ich vergesse, welche Namen Sie verwendet haben), also nenne ich nur das erste Mitglied _ – kurz, konsequent, abseits und nicht sonderlich umständlich. (Der Name könnte tatsächlich vom Standard und/oder Compiler reserviert sein, aber ich bezweifle, dass irgendjemand ihn verwenden würde, wenn dies der Fall wäre.)

    – Chris Lutz

    10. September 2011 um 8:56 Uhr

  • Wenn image ist lästig, viel zu tippen, verwenden img. Verwenden Sie Suchen und Ersetzen, um dies für Sie zu tun. Keine Sorge – was auch immer Sie tun müssen, Sie werden es überleben.

    – Chris Lutz

    10. September 2011 um 9:09 Uhr

  • @chacham15 Alter, vermeide es, eine C-Frage als C++ zu markieren. Das sind zwei völlig unterschiedliche Sprachen.

    – amit kumar

    10. September 2011 um 9:21 Uhr

  • Ja, ich habe das Etikett schnell entfernt

    – chacham15

    10. September 2011 um 10:00 Uhr

  • Sie müssen nicht verwenden foo_s für den Strukturnamen. typedef struct foo { ... } foo; funktioniert gut.

    – 12Me21

    2. November 2018 um 22:35 Uhr

Offensichtlich diese Funktion wurde C11 hinzugefügtaber leider habe ich keinen Zugriff auf einen C-Compiler der letzten Jahre (>= GCC 4.6.2).

typedef struct foo {
  int a;
} foo;

typedef struct bar {
  struct foo;
  int b;
} bar;

int main() {
  bar b;
  b.a = 42;
  b.b = 99;
  return 0;
}

  • Beachten Sie, dass dies funktioniert, weil innerhalb bar, struct foo wird anonym (dh ohne Variablennamen) verwendet.

    – Frank Küster

    14. September 2017 um 10:22 Uhr

  • Wie im verlinkten Dokument erklärt, ist dies eine Microsoft-Erweiterung und kompiliert nicht auf GCC und Clang ohne die -fms-extensions Flagge.

    – jcsahnwaldt Wiedereinsetzung von Monica

    5. März 2019 um 15:39 Uhr

Mit Zeigern ist das möglich, da ein Zeiger auf ein Strukturobjekt garantiert auf sein erstes Mitglied zeigt. Siehe zB Dieser Artikel.

#include <stdlib.h>
#include <stdio.h>

typedef struct foo_s {
    int a;
} foo;

typedef struct bar_s {
    foo super;
    int b;
} bar;

int fooGetA(foo *x) {
  return x->a;
}

void fooSetA(foo *x, int a) {
  x->a = a;
}

int main() {
  bar* derived = (bar*) calloc(1, sizeof(bar));
  fooSetA((foo*) derived, 5);
  derived->b = 3;
  printf("result: %d\n", fooGetA((foo*) derived));
  return 0;
}

  • Es ist noch besser, wenn sie nehmen void * Zeiger und vermeiden Sie das Casting auf der Anruferseite. (Die Werte einiger Leute von „besser“ können sich von meinen unterscheiden.)

    – Chris Lutz

    10. September 2011 um 9:03 Uhr


  • Ja, es ist sicherlich bequemer für den Anrufer. Ich wollte zeigen, dass der Autor der foo-Funktionen nicht wissen muss, dass die “echte” Struktur etwas anderes als foo ist.

    – Jouni K. Seppänen

    10. September 2011 um 9:38 Uhr

  • Nun, beide Ansätze sind richtig und falsch zugleich. Die Typumwandlung zwingt den Client-Code zu einer geringen Qualität. Dies kann mit gelöst werden void *, aber das eliminiert die Typsicherheit, die noch schlimmer ist. Dennoch sind dies eine nette Antwort und einige gute Argumente, insbesondere Jounis letzter Kommentar, wenn es um Abstraktionen (den Vererbungspunkt) geht, aber die oben genannten Faktoren müssen berücksichtigt werden. Hier ist die C11-Lösung: modelingwithdata.org/arch/00000113.htm; Ich frage mich, ob es eine Problemumgehung für andere Versionen von C gab

    – Machtsklave

    2. Dezember 2013 um 17:06 Uhr


Nicht möglich im C so wie du es getan hast. Aber Sie können die Vererbung mit a nachahmen foo Mitgliedsvariable ein bar.

typedef struct bar_s {
    foo obj;
    int b;
} bar;

bar b;
b.obj.a = 10;

  • Das heißt Komposition wenn ich mich nicht irre.

    – michaelsnowden

    25. Januar 2014 um 1:14 Uhr

  • Ich denke, das hat der OP gemacht nicht wollen; es ist die “offensichtliche Lösung”.

    – U. Windl

    19. August 2021 um 12:28 Uhr

Benutzer-Avatar
Kerl L

Wenn Sie meinen

typedef struct foo_s {
    int a;
  } foo;

typedef struct bar_s {
 foo my_foo;
int b;
} bar;

also kannst du machen:

bar b; b.my_foo.a = 3;

Andernfalls gibt es seit dem keine Möglichkeit, dies in C zu tun sizeof(bar_s) ist nachteilig für die Kompilierzeit. Es ist keine gute Praxis, aber Sie können eine sparen void * ptr; Zeiger innerhalb von bar_s und eine weitere Aufzählung, die die beschreibt ptr Typ, und durch den Typ umgewandelt.

dh:

typedef enum internalType{
  INTERNAL_TYPE_FOO = 0,
}internalType_t;

typedef struct bar_s {
 internalType_t ptrType;
 void* ptr;
int b;
} bar;

und dann:

bar b;  foo f;
b.ptrType = INTERNAL_TYPE_FOO;
b.ptr = &f;

und an anderer Stelle im Code:

 if (b.ptrType == INTERNAL_TYPE_FOO) {
    foo* myFooPtr = (foo *)b.ptr;
 }

Das geht ganz einfach per Präprozessor:

Erstellen Sie eine Datei mit dem Namen base_foo.h:

int foo;

Dann einfach einfügen:

typedef struct foo_s {
    #include "base_foo.h"
} foo;

typedef struct bar_s {
    #include "base_foo.h"
    int b;
} bar;

  • Sie müssen keine zusätzliche Datei erstellen, Sie können Strukturfelder mehrzeilig definieren

    – Maxim Akristiny

    29. Juni 2018 um 10:38 Uhr

  • Trotzdem ist diese Lösung etwas hässlich.

    – U. Windl

    19. August 2021 um 12:32 Uhr

  • @U.Windl Ja, Sie können auch einfach ein schnelles Skript schreiben, das “richtige” Strukturen generiert, die einfach dasselbe Speicherlayout verwenden … wären nicht mehr als ein paar Zeilen JavaScript, um alle Ihre erweiterten C-Strukturen zu generieren .

    – kungfooman

    19. August 2021 um 13:01 Uhr


Benutzer-Avatar
Abraham Le

Es gibt eine Verwechslung zwischen anonymen Strukturen und Vereinigungen mit namenlosem Feld. Das namenlose Feld ist eine Microsoft-Erweiterung.

struct known {
    struct /* anonymous */ {
        int anonymous;
    };
    int known;
};

Ein anonymer struct oder union ist ein struct oder union ohne irgendetwas tag name das in ein anderes eingebettet ist struct oder union. Es muss auch keine Feldnamen haben.

Ein namenloses Feld ist eine Microsoft-Erweiterung, die eine eingeschränkte Vererbung in C ermöglicht.

struct A {
    int a;
};

struct B {
    struct A: // nameless field
    int b;
};

Anonym struct oder union sind keine namenlosen Felder, und namenlose Felder sind nicht anonym, zumindest so, wie es der C11-Standard definiert.

  • Sie müssen keine zusätzliche Datei erstellen, Sie können Strukturfelder mehrzeilig definieren

    – Maxim Akristiny

    29. Juni 2018 um 10:38 Uhr

  • Trotzdem ist diese Lösung etwas hässlich.

    – U. Windl

    19. August 2021 um 12:32 Uhr

  • @U.Windl Ja, Sie können auch einfach ein schnelles Skript schreiben, das “richtige” Strukturen generiert, die einfach dasselbe Speicherlayout verwenden … wären nicht mehr als ein paar Zeilen JavaScript, um alle Ihre erweiterten C-Strukturen zu generieren .

    – kungfooman

    19. August 2021 um 13:01 Uhr


Benutzer-Avatar
neudelphi

Sie können versuchen, die Vererbung zu verwenden:

struct foo_s
{
    int a;
};

struct bar_s: foo_a
{
    int b;
};

Funktioniert in C++, nicht sicher, ob es in C funktioniert.

  • mein schlechtes, du hast recht, ich weiß nicht, warum ich das c++-tag hinzugefügt habe, ich meinte c. sry!

    – chacham15

    10. September 2011 um 8:37 Uhr

  • Vererbung funktioniert in C AFAIK nicht. Ich vermute, das OP wollte genau den Vererbungsmechanismus von C++ in C nachahmen.

    – U. Windl

    19. August 2021 um 12:37 Uhr

1257250cookie-checkKann ich eine Struktur in C “erweitern”?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy