Array der Länge Null

Lesezeit: 7 Minuten

Array der Lange Null
bgee

Ich arbeite an der Umgestaltung von altem Code und habe einige Strukturen gefunden, die Arrays der Länge Null enthalten (unten). Warnungen werden natürlich durch Pragma unterdrückt, aber ich habe es versäumt, durch “neue” Strukturen zu erstellen, die solche Strukturen enthalten (Fehler 2233). Array ‘byData’ als Zeiger verwendet, aber warum nicht stattdessen Zeiger verwenden? oder Array der Länge 1? Und natürlich wurden keine Kommentare hinzugefügt, damit ich den Prozess genießen könnte … Gibt es Gründe, so etwas zu verwenden? Irgendwelche Ratschläge beim Refactoring dieser?

struct someData
{
   int nData;
   BYTE byData[0];
}

NB: Es ist C++, Windows XP, VS 2003

  • Dies ist der in Frage 2.6 beschriebene “struct hack”. comp.lang.c FAQ. Dennis Ritchie nannte es “ungerechtfertigte Kumpanei mit der C-Implementierung”. C99 führte eine neue Sprachfunktion ein, das “flexible Array Member”, um den Struct-Hack zu ersetzen. Sogar der Compiler von Microsoft, der für seine fehlende C99-Unterstützung bekannt ist, unterstützt flexible Array-Member.

    – Keith Thompson

    11. September 2012 um 18:37 Uhr

Array der Lange Null
Martin York

Ja, das ist ein C-Hack.
So erstellen Sie ein Array beliebiger Länge:

struct someData* mallocSomeData(int size)
{
    struct someData*  result = (struct someData*)malloc(sizeof(struct someData) + size * sizeof(BYTE));
    if (result)
    {    result->nData = size;
    }
    return result;
}

Jetzt haben Sie ein Objekt von someData mit einem Array einer bestimmten Länge.

  • Sollte das nicht zumindest nützen new[]geht es hier um C++?

    – abschalten

    24. Februar 2014 um 14:21 Uhr

  • @unwind: Kann dafür nicht neu verwenden. Der springende Punkt ist, dass dies ein C-Hack ist und in C++ nicht erforderlich ist (weil wir bessere Möglichkeiten haben, dies zu tun). Ich bin mir auch ziemlich sicher, dass Arrays der Länge Null in C++ illegal sind (zumindest C++03, nicht sicher, ob das in C++11 aktualisiert wurde).

    – Martin York

    28. Dezember 2014 um 17:17 Uhr

  • Der gängige Fachjargon dafür ist „Struct Hack“.

    – Krishna Oza

    16. Januar 2015 um 4:27 Uhr

  • Außer, Ihre Berechnung ist ausgeschaltet (im allgemeinen Fall). Abhängig vom Typ der Objekte im Array muss ein Compiler bestimmte Ausrichtungsregeln auferlegen, und das Summieren der Größen der Mitglieder ergibt möglicherweise nicht die richtige Größe. Verwenden Sie stattdessen die Offsetvon Makro, damit der Compiler das richtige Ergebnis berechnet. (Hinweis: Kein Problem für BYTEs, vorausgesetzt, diese sind als einige definiert char Variante.)

    – Inspektionsfähig

    14. Mai 2016 um 23:24 Uhr

1646889608 606 Array der Lange Null
JaredPar

Es gibt leider mehrere Gründe, warum Sie ein Array der Länge Null am Ende einer Struktur deklarieren würden. Es gibt Ihnen im Wesentlichen die Möglichkeit, eine Struktur mit variabler Länge von einer API zurückzugeben.

Raymond Chen hat einen ausgezeichneten Blogbeitrag zu diesem Thema verfasst. Ich schlage vor, dass Sie sich diesen Beitrag ansehen, da er wahrscheinlich die gewünschte Antwort enthält.

Beachten Sie in seinem Beitrag, dass es sich um Arrays der Größe 1 anstelle von 0 handelt. Dies ist der Fall, weil Arrays der Länge Null ein neuerer Eintrag in die Standards sind. Sein Beitrag sollte immer noch auf Ihr Problem zutreffen.

http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx

BEARBEITEN

Hinweis: Obwohl Raymonds Beitrag besagt, dass Arrays der Länge 0 in C99 legal sind, sind sie in C99 tatsächlich immer noch nicht legal. Anstelle eines Arrays der Länge 0 sollten Sie hier ein Array der Länge 1 verwenden

  • Dies ist der Fall, weil Arrays der Länge Null ein neuerer Eintrag in die Standards sind.” Welche Standards? C++11 verbietet immer noch Arrays der Länge 0 (§8.3.4/1) sowie C99 (§6.7.5.2/1).

    – Ildjarn

    13. April 2012 um 16:42 Uhr


  • @ildjarn Ich habe im Wesentlichen nachgeplappert, was Raymond am Ende seines Blogbeitrags gesagt hat. Ich war mir nicht bewusst, dass Arrays mit der Länge 0 in C99 immer noch illegal waren, bis ich kürzlich eine Kommentardiskussion mit Ihnen zu einer anderen Frage hatte. Ich werde die Antwort aktualisieren

    – JaredPar

    13. April 2012 um 16:47 Uhr

  • Tut mir leid, eine so alte Antwort zu kritisieren. :-PI frage nur, weil eine andere Frage hier als “Beweis” verlinkt ist, dass Arrays der Länge 0 legales C++ waren. 🙂

    – Ildjarn

    13. April 2012 um 16:48 Uhr

  • @ildjarn NP über pingelig alte Antworten. Ich möchte auf keinen Fall schlechte Daten verbreiten 🙂

    – JaredPar

    13. April 2012 um 16:49 Uhr

  • Können Sie JEDE Referenz zu den Auswirkungen der Verwendung von 0-Längen finden oder [] Arrays auf Speicherausrichtung? In einem Projekt hat ein Kollege herausgefunden, dass der Einsatz am besten sicher wäre int arr[] (da int vor Ausrichtungsproblemen schützt), aber da wir das Array zurückgeben, ist in unserem Fall das Allerbeste void *arr[]was ziemlich kryptografisch ist.

    – Spidy

    14. Juli 2012 um 22:24 Uhr

Dies ist ein alter C-Hack, um Arrays mit flexibler Größe zu ermöglichen.

Im C99-Standard ist dies nicht erforderlich, da er die arr unterstützt[] Syntax.

  • Leider ist Visual Studio sehr schlecht, wenn es um C99-Unterstützung geht. 🙁

    – Alex B

    17. November 2008 um 7:04 Uhr

  • Ohne auf die allgemeine Wahrheit Ihres Kommentars einzugehen, … unterstützt der MS VC v9-Compiler den arr[] Syntax.

    – Käse

    14. Dezember 2009 um 17:18 Uhr

1646889609 235 Array der Lange Null
Kaz

Ihre Intuition zu “Warum nicht ein Array der Größe 1 verwenden” ist genau richtig.

Der Code macht den “C-Struktur-Hack” falsch, da Deklarationen von Arrays der Länge Null eine Einschränkungsverletzung darstellen. Dies bedeutet, dass ein Compiler Ihren Hack direkt zur Kompilierzeit mit einer Diagnosemeldung ablehnen kann, die die Übersetzung stoppt.

Wenn wir einen Hack ausführen wollen, müssen wir ihn am Compiler vorbeischleichen.

Der richtige Weg, den “C-Struktur-Hack” durchzuführen (der mit C-Dialekten kompatibel ist, die bis 1989 ANSI C und wahrscheinlich viel früher zurückreichen), besteht darin, ein vollkommen gültiges Array der Größe 1 zu verwenden:

struct someData
{
   int nData;
   unsigned char byData[1];
}

Außerdem statt sizeof struct someDatadie Größe des Teils vor byData wird berechnet mit:

offsetof(struct someData, byData);

A zuzuweisen struct someData mit Platz für 42 Bytes in byDatawürden wir dann verwenden:

struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42);

Beachten Sie, dass dies offsetof Die Berechnung ist tatsächlich die richtige Berechnung, selbst wenn die Array-Größe Null ist. Siehst du, sizeof die gesamte Struktur kann eine Polsterung enthalten. Wenn wir zum Beispiel so etwas haben:

struct hack {
  unsigned long ul;
  char c;
  char foo[0]; /* assuming our compiler accepts this nonsense */
};

Die Größe von struct hack ist wegen der evtl. zur Ausrichtung gepolstert ul Mitglied. Wenn unsigned long vier Bytes breit ist, dann durchaus möglich sizeof (struct hack) ist 8, wohingegen offsetof(struct hack, foo) ist mit ziemlicher Sicherheit 5. Die offsetof Methode ist der Weg, um die genaue Größe des vorhergehenden Teils der Struktur direkt vor dem Array zu erhalten.

Das wäre also der Weg, den Code umzugestalten: ihn an den klassischen, hochportablen Struct-Hack anzupassen.

Warum nicht einen Zeiger verwenden? Weil ein Zeiger zusätzlichen Platz belegt und initialisiert werden muss.

Es gibt noch andere gute Gründe, einen Zeiger nicht zu verwenden, nämlich dass ein Zeiger einen Adressraum benötigt, um sinnvoll zu sein. Der Struct-Hack ist externalisierbar: Das heißt, es gibt Situationen, in denen ein solches Layout externen Speichern wie Dateibereichen, Paketen oder gemeinsam genutztem Speicher entspricht, in denen Sie keine Zeiger haben möchten, weil sie nicht aussagekräftig sind.

Vor einigen Jahren habe ich den Struct-Hack in einer Shared-Memory-Message-Passing-Schnittstelle zwischen Kernel und Userspace verwendet. Ich wollte dort keine Zeiger, weil sie nur für den ursprünglichen Adressraum des Prozesses, der eine Nachricht generiert, von Bedeutung gewesen wären. Der Kernel-Teil der Software hatte einen Blick auf den Speicher, indem er ein eigenes Mapping an einer anderen Adresse verwendete, und so basierte alles auf Offset-Berechnungen.

Es lohnt sich, IMO auf den besten Weg zur Größenberechnung hinzuweisen, der im oben verlinkten Artikel von Raymond Chen verwendet wird.

struct foo
{
    size_t count;
    int data[1];
}

size_t foo_size_from_count(size_t count)
{
    return offsetof(foo, data[count]);
}

Der Versatz des ersten Eintrags vom Ende der gewünschten Zuordnung ist auch die Größe der gewünschten Zuordnung. IMO ist es eine äußerst elegante Art, die Größenberechnung durchzuführen. Es spielt keine Rolle, welchen Elementtyp das Array mit variabler Größe hat. Der offsetof (oder FIELD_OFFSET oder UFIELD_OFFSET in Windows) wird immer gleich geschrieben. Keine sizeof()-Ausdrücke, die versehentlich durcheinandergebracht werden könnten.

986360cookie-checkArray der Länge Null

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

Privacy policy