So initialisieren Sie eine Struktur mit einem flexiblen Array-Mitglied

Lesezeit: 6 Minuten

Ich habe folgende Struktur

typedef struct _person {
    int age;
    char sex;
    char name[];
}person;

Ich habe eine grundlegende Internetsuche (aber erfolglos) durchgeführt, um eine Instanz zu erstellen und eine Struktur mit einem flexiblen Array-Mitglied ohne Verwendung zu initialisieren malloc().

Zum Beispiel: für normale Strukturen wie

struct a {
    int age; 
    int sex;
};

Wir können eine Instanz von erstellen struct a und initialisieren Sie es wie

struct a p1 = {10, 'm'};

Aber für Strukturen mit flexiblem Array darin (wie _person wie oben erwähnt), wie können wir eine Instanz erstellen und initialisieren, wie wir es normalerweise tun structures?

Ist es überhaupt möglich? Wenn ja, wie übergeben wir bei der Initialisierung die Array-Größe und den eigentlich zu initialisierenden Wert?

(oder)

Stimmt es, dass die einzige Möglichkeit, eine Struktur mit flexiblem Array zu erstellen, die Verwendung von ist malloc() wie in der C99-Spezifikation erwähnt – 6.7.2.1 Structure and union specifiers - point #17?!

  • Sie können nicht, struct muss die Größe der Kompilierzeit haben.

    – Irgendein Korn

    31. Dezember 2011 um 10:48 Uhr

  • @Anycorn: Strukturen mit flexiblen Array-Mitgliedern haben eine Kompilierzeitgröße.

    – CB Bailey

    31. Dezember 2011 um 10:56 Uhr

  • GCC hat eine Erweiterung, mit der Sie so etwas tun können struct { size_t len; int data[]; } x = { 4, { 1, 2, 3, 4 } }; und es wird funktionieren, aber es ist nicht tragbar. Sie können sich jederzeit die Version Ihrer Plattform ansehen alloca für eine möglicherweise tragbarere Lösung, aber Sie müssten sicherstellen, dass sie sich alle gleich verhalten und die gleichen Implementierungsmacken haben.

    – Chris Lutz

    31. Dezember 2011 um 10:58 Uhr

  • @Charles nicht im Sinne des Posters – es muss eine Art Malloc oder Äquivalent geben.

    – Irgendein Korn

    31. Dezember 2011 um 11:30 Uhr

  • gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Zero-Length.html Es scheint standardmäßig aktiviert zu sein (vorausgesetzt, Sie verwenden nicht -ansi oder irgendetwas, das es deaktivieren würde).

    – Chris Lutz

    31. Dezember 2011 um 12:22 Uhr

Benutzer-Avatar
Jens Gustedt

Nein, flexible Arrays müssen immer manuell zugewiesen werden. Aber Sie können verwenden calloc zum Initialisieren des flexiblen Teils und ein zusammengesetztes Literal zum Initialisieren des festen Teils. Ich würde das in eine Zuordnung packen inline funktionieren so:

typedef struct person {
  unsigned age;
  char sex;
  size_t size;
  char name[];
} person;

inline
person* alloc_person(int a, char s, size_t n) {
  person * ret = calloc(sizeof(person) + n, 1);
  if (ret) memcpy(ret,
                  &(person const){ .age = a, .sex = s, .size = n},
                  sizeof(person));
  return ret;
}

Beachten Sie, dass die Prüfung, ob die Zuweisung erfolgreich war, dem Anrufer bleibt.

Wenn Sie keine benötigen size Feld, wie ich es hier eingefügt habe, würde sogar ein Makro ausreichen. Nur dass es nicht möglich wäre, die Rücksendung zu überprüfen calloc bevor Sie das tun memcpy. Unter allen Systemen, die ich bisher programmiert habe, bricht dies relativ schön ab. Generell denke ich das Rückkehr von malloc ist von untergeordneter Bedeutungaber die Meinungen zu diesem Thema gehen weit auseinander.

Dies könnte (in diesem speziellen Fall) dem Optimierer vielleicht mehr Möglichkeiten geben, den Code in die Umgebung zu integrieren:

#define ALLOC_PERSON(A,  S,  N)                                 \
((person*)memcpy(calloc(sizeof(person) + (N), 1),               \
                 &(person const){ .age = (A), .sex = (S) },     \
                 sizeof(person)))

Bearbeiten: Der Fall, dass dies besser sein könnte als die Funktion ist, wenn A und S sind Kompilierzeitkonstanten. In diesem Fall das zusammengesetzte Literal, da es so ist const qualifiziert, könnte statisch zugewiesen werden und seine Initialisierung könnte zur Kompilierzeit erfolgen. Wenn außerdem mehrere Zuweisungen mit denselben Werten im Code erscheinen würden, wäre es dem Compiler erlaubt, nur eine einzige Kopie dieses zusammengesetzten Literals zu realisieren.

  • Dabei eine ungeprüfte Kopie des Ergebnisses von calloc() ist gefährlich; Wenn die Zuordnung fehlschlägt, erhalten Sie einen Core-Dump (oder ein anderes undefiniertes Verhalten, das wahrscheinlich nicht das ist, was Sie wollten).

    – Jonathan Leffler

    31. Dezember 2011 um 16:11 Uhr

  • @JonathanLeffler, richtig, wird ändern. Ich werde das in die Funktion integrieren. Für das Makro beziehe ich mich nur auf meine generische Tirade über die Überprüfung der Rückgabe von malloc.

    – Jens Gustedt

    31. Dezember 2011 um 16:31 Uhr

  • Ihre Inline-Funktionslösung ist großartig. Das Makro hat meiner Meinung nach keinen Vorteil. Es ist nicht lesbar, überprüft den Calloc-Rückgabewert nicht und wird keine bessere Leistung erbringen. Makros funktionieren im Allgemeinen nicht besser als Inline-Funktionen (manchmal schlechter – ziehen Sie in Betracht, strlen() an ein Makro zu übergeben, das es zweimal auswertet).

    – ugoren

    31. Dezember 2011 um 17:38 Uhr

  • @ugoren, const Qualifizierte zusammengesetzte Literale sind speziell in Bezug auf die Optimierung. Mit ihnen ist mehr Optimierung zulässig, siehe meine Bearbeitung. Man könnte wahrscheinlich versuchen, beide Ansätze zu kombinieren, indem man eine Funktion hat, die so etwas tut calloc_and_copy_or_fail.

    – Jens Gustedt

    31. Dezember 2011 um 17:52 Uhr


  • Nett. BTW, statt person *ret = calloc(sizeof(person) + n, 1);ist eine sicherere Angewohnheit zu schreiben person *ret = calloc(sizeof(*ret) + (sizeof(ret->name[0]) * n), 1);.

    – Todd Lehmann

    11. Juli 2015 um 6:34 Uhr


Benutzer-Avatar
Giuseppe Guerrini

Es gibt einige Tricks, die Sie anwenden können. Es hängt von Ihrer speziellen Anwendung ab.

Wenn Sie eine einzelne Variable initialisieren möchten, können Sie eine Struktur in der richtigen Größe definieren:

   struct  {
        int age;
        char sex;
        char name[sizeof("THE_NAME")];
    } your_variable = { 55, 'M', "THE_NAME" };

Das Problem ist, dass Sie Pointer-Casting verwenden müssen, um die Variable als “Person” zu interpretieren (z. B. “*(person *)(&your_variable)”. Aber Sie können eine enthaltende Vereinigung verwenden, um dies zu vermeiden:

union {
 struct { ..., char name[sizeof("THE_NAME")]; } x;
 person p;
} your_var = { 55, 'M', "THE_NAME" };

Ihre_var.p ist also vom Typ “Person”. Sie können auch ein Makro verwenden, um Ihren Initialisierer zu definieren, sodass Sie die Zeichenfolge nur einmal schreiben müssen:

#define INIVAR(x_, age_, sex_ ,s_) \
   union {\
     struct { ..., char name[sizeof(s_)]; } x;\
     person p;\
    } x_ = { (age_), (sex_), (s_) }

INIVAR(your_var, 55, 'M', "THE NAME");

Ein weiteres Problem ist, dass dieser Trick nicht dazu geeignet ist, ein Array von “Personen” zu erstellen. Das Problem bei Arrays ist, dass alle Elemente die gleiche Größe haben müssen. In diesem Fall ist es sicherer, a zu verwenden const char * anstelle einer char[]. Oder nutzen Sie die dynamische Zuordnung 😉

Ein Strukturtyp mit einem flexiblen Arraymitglied kann so behandelt werden, als ob das flexible Arraymitglied weggelassen würde, sodass Sie die Struktur wie folgt initialisieren können.

person p = { 10, 'x' };

Es sind jedoch keine Mitglieder des flexiblen Arrays zugeordnet, und jeder Versuch, auf ein Mitglied des flexiblen Arrays zuzugreifen oder einen Zeiger auf eines hinter seinem Ende zu bilden, ist ungültig. Die einzige Möglichkeit, eine Instanz einer Struktur mit einem flexiblen Array-Member zu erstellen, das tatsächlich Elemente in diesem Array enthält, besteht darin, ihm dynamisch Speicher zuzuweisen, z. B. mit malloc.

  • Es gibt eine GCC-Erweiterung, mit der Sie das flexible Array-Mitglied mit derselben Syntax angeben können, wenn Sie darauf stehen.

    – Chris Lutz

    31. Dezember 2011 um 10:59 Uhr

1382090cookie-checkSo initialisieren Sie eine Struktur mit einem flexiblen Array-Mitglied

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

Privacy policy