Sind flexible Array-Member in C++ gültig?

Lesezeit: 9 Minuten

In C99 können Sie ein flexibles Array-Mitglied einer Struktur als solches deklarieren:

struct blah
{
    int foo[];
};

Als jedoch jemand hier bei der Arbeit versuchte, Code mit clang in C++ zu kompilieren, funktionierte diese Syntax nicht. (Es hat mit MSVC funktioniert.) Wir mussten es konvertieren in:

struct blah
{
    int foo[0];
};

Beim Durchsehen des C++-Standards fand ich überhaupt keinen Hinweis auf flexible Member-Arrays; Ich dachte immer [0] war eine ungültige Deklaration, aber anscheinend ist sie für ein flexibles Member-Array gültig. Sind flexible Member-Arrays in C++ tatsächlich gültig? Wenn ja, ist die richtige Erklärung [] oder [0]?

  • Kannst du nicht einfach a verwenden std::vector<int> Mitglied und kümmern Sie sich um interessantere Sachen? Oder ist das ein Layoutproblem?

    – fredoverflow

    10. Dezember 2010 um 19:59 Uhr

  • Dieses flexible-Array-Member-Tag scheint ein bisschen … einsam zu sein. Aber vielleicht liegt es nur an mir.

    – Markus Borkenhagen

    10. Dezember 2010 um 20:09 Uhr


  • @FredOverflow: Manchmal müssen Strukturen vorhanden sein, die sowohl in C als auch in C++ verwendet werden können (System-APIs sind ein sehr häufiges Beispiel).

    – Michael Burr

    10. Dezember 2010 um 20:45 Uhr

  • @FredOverflow, normalerweise würde ich das tun, aber in diesem Fall ist eine zusammenhängende Zuweisung für erforderlich blah mit variabler Größe foo. Es ist sicherlich eine gute Designfrage, warum wir es überhaupt brauchen, auf die ich hier nicht eingehen kann.

    – MSN

    10. Dezember 2010 um 21:11 Uhr

  • Eigentlich ist diese spezifische Konstruktion gemäß der illegal C99-Standardwie es heißt: “als Sonderfall das letzte Element einer Struktur mit mehr als eine Named Member kann einen unvollständigen Array-Typ haben; das nennt man a flexibles Array-Mitglied.” Damit blah braucht ein zusätzliches Mitglied, eins davor fooum in C99 gültig zu sein.

    – Hallo Auf Wiedersehen

    18. Oktober 2020 um 20:24 Uhr


Sind flexible Array Member in C gultig
Martin v. Löwen

C++ wurde erstmals 1998 standardisiert, also vor der Hinzufügung flexibler Array-Mitglieder zu C (was neu in C99 war). 2003 gab es eine Berichtigung zu C++, die jedoch keine relevanten neuen Funktionen hinzufügte. Die nächste Revision von C++ (C++2b) befindet sich noch in der Entwicklung, und es scheint, dass ihr noch keine flexiblen Array-Mitglieder hinzugefügt werden.

  • Können Sie diese Antwort bitte aktualisieren? Es scheint, dass C++11 keine flexiblen Array-Mitglieder hinzugefügt hat (§9.2/9), und es sieht so aus, als würde C++14 dasselbe sein.

    – Adam Rosenfield

    12. November 2014 um 7:03 Uhr

  • Und C++17 hat sie auch nicht, aber ich denke, sie werden immer noch untersucht, also vielleicht C++2a?

    – DanielH

    7. November 2017 um 23:01 Uhr

  • Irgendeine Möglichkeit, GCC zu machen g++ eine Warnung geben? -std=c++xx -Wall -Wextra ist nicht genug. Unheimlich 🙁

    – Ciro Santilli 新疆再教育营六四事件法轮功郝海东

    25. Oktober 2018 um 16:49 Uhr


  • @CiroSantilli新疆改造中心六四事件法轮功 Wenn ich mich richtig erinnere -Wpedantic oder -pedantic wird die Warnung auslösen.

    – Werner Henze

    4. Januar 2019 um 14:50 Uhr

  • Immer noch nichts in C ++ 20, obwohl ein Papier existierte: thephd.github.io/vendor/future_cxx/papers/d1039.html

    – Flammenfeuer

    7. Oktober 2019 um 15:36 Uhr

1646638815 582 Sind flexible Array Member in C gultig
Michael Burr

C++ unterstützt keine flexiblen C99-Array-Member am Ende von Strukturen, entweder mit einer leeren Indexnotation oder a 0 Indexnotation (ausgenommen herstellerspezifische Erweiterungen):

struct blah
{
    int count;
    int foo[];  // not valid C++
};

struct blah
{
    int count;
    int foo[0]; // also not valid C++
};

Soweit ich weiß, wird C++0x dies auch nicht hinzufügen.

Wenn Sie das Array jedoch auf 1 Element skalieren:

struct blah
{
    int count;
    int foo[1];
};

Der Code wird kompiliert und funktioniert recht gut, aber es ist ein technisch undefiniertes Verhalten. Sie können den entsprechenden Speicher mit einem Ausdruck zuweisen, der wahrscheinlich keine Off-by-One-Fehler enthält:

struct blah* p = (struct blah*) malloc( offsetof(struct blah, foo[desired_number_of_elements]);
if (p) {
    p->count = desired_number_of_elements;

    // initialize your p->foo[] array however appropriate - it has `count`
    // elements (indexable from 0 to count-1)
}

Es ist also zwischen C90, C99 und C++ portierbar und funktioniert genauso gut wie die flexiblen Array-Mitglieder von C99.

Raymond Chen hat dazu einen netten Bericht geschrieben: Warum enden manche Strukturen mit einem Array der Größe 1?

Hinweis: In Raymond Chens Artikel gibt es einen Tippfehler/Fehler in einem Beispiel, das das „flexible“ Array initialisiert. Es sollte lauten:

for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
  TokenGroups->Groups[Index] = ...;
}

  • Aber selbst wenn Sie überschüssigen Speicher zuweisen, können Sie immer noch nicht gültig auf Elemente außerhalb der Array-Grenzen eines Elements zugreifen. Das Verhalten ist undefiniert; Eine C++-Implementierung hätte das Recht, eine Begrenzungsprüfung gemäß dem tatsächlichen Typ des konstruierten Objekts hinzuzufügen.

    – CB Bailey

    10. Dezember 2010 um 21:14 Uhr

  • @Charles – Ich glaube nicht, dass Sie damit Recht haben (auch nicht pedantisch), sonst wäre das Folgende ein undefiniertes Verhalten: int* p = malloc(sizeof(int)*4); p[3] = 0;.

    – Michael Burr

    10. Dezember 2010 um 22:01 Uhr

  • @Michael: Ich denke, das ist der Grund, warum Charles sagte ein element ist nicht, weil er denkt, dass es unmöglich ist, ein Array zuzuweisen, sondern weil 1 die Länge des Arrays in Ihrer speziellen Struktur ist, blah. Der Anspruch ist da p->foo ist vom Typ blah[1]dann p->foo[1] ist U.B. Wie auch immer, obwohl p->foo[1] außerhalb des Objekts liegt fooes ist nicht außerhalb des Arrays von char das wurde mit zugeordnet mallocso dass es ist innerhalb eines Objekts. Mit passenden Güssen über char* Zumindest Lesezugriff wäre in Ordnung. Ich kann mich jedoch nicht erinnern, wie die Standards Legalese herausfallen.

    – Steve Jessop

    10. Dezember 2010 um 22:23 Uhr

  • Nun, ich schätze, ich muss meine Worte schlucken. Kein anderer als WG14 hat angegeben, dass dies UB ist (Fehlerbericht 51: open-std.org/Jtc1/sc22/wg14/www/docs/dr_051.html). Ich behaupte jedoch, dass 1) die von WG14 in DR51 vorgeschlagene „sicherere Ausdrucksweise“ absolut lächerlich ist, 2) sich die UB auf allen für mich wichtigen Plattformen wie erwartet verhält und 3) Alternativen (die ebenfalls keine UB sind ) sind weniger bequem und/oder fehleranfälliger zu verwenden (und verursachen daher eher beobachtbare Fehler) – daher werde ich es wahrscheinlich weiterhin verwenden. Aber jetzt weiß ich wenigstens, dass ich eine Regel breche…

    – Michael Burr

    10. Dezember 2010 um 22:59 Uhr

  • Ja, ich habe nicht behauptet, dass es keine nützliche Technik ist, nur dass es nicht streng konform ist. Leider ist aus einem anderen Grund auch die sicherere Redewendung UB. Sie können Zeigerarithmetik nur für ein Objekt ausführen, das tatsächlich existiert, und ein POD-Objekt beginnt erst zu existieren, wenn der Speicher ausreichend ausgerichtet ist und Größe zugeordnet ist. Wenn etwas mit einem sehr großen Array deklariert wird und Sie ihm nicht genügend Speicherplatz zuweisen, kann es nicht existieren.

    – CB Bailey

    10. Dezember 2010 um 23:46 Uhr


Der zweite enthält keine Elemente, sondern zeigt direkt danach blah. Wenn Sie also eine Struktur wie diese haben:

struct something
{
  int a, b;
  int c[0];
};

Sie können Dinge wie folgt tun:

struct something *val = (struct something *)malloc(sizeof(struct something) + 5 * sizeof(int));
val->a = 1;
val->b = 2;
val->c[0] = 3;

In diesem Fall c verhält sich wie ein Array mit 5 ints, aber die Daten im Array werden nach dem sein something Struktur.

Das Produkt, an dem ich arbeite, verwendet dies als Zeichenfolge mit der Größe:

struct String
{
  unsigned int allocated;
  unsigned int size;
  char data[0];
};

Aufgrund der unterstützten Architekturen verbraucht dies mehr als 8 Bytes allocated.

Natürlich ist das alles C, aber g++ zum Beispiel akzeptiert es ohne Probleme.

  • Das ist ziemlich interessant. Ich kann mir vorstellen, dass Sie diese Struktur niemals als Wert an eine Funktion übergeben können, oder? da würde das wohl gerade vorbei gehen sizeof(String) die die von Ihnen zugewiesene Größe nicht berücksichtigen würden data. Aber es sollte funktionieren, solange Sie es nur als Referenz oder Zeiger übergeben, ist das richtig?

    – Philipp

    10. Dezember 2010 um 20:15 Uhr

  • Das ist nicht wahr. T[0] ist weder in C noch in C++ ein gültiger Typbezeichner. Sie müssen verwenden T[].

    – Johannes Schaub – litb

    11. Dezember 2010 um 10:35 Uhr

  • @Johannes es ist in C99 gültig, siehe open-std.org/jtc1/sc22/wg14/www/newinc9x.htm

    – Endstation

    11. Dezember 2010 um 11:10 Uhr

  • Of course all this is C but g++ for example accepts it without a hitch. Das ist schön für g++. Laut Standard ist es immer noch UB und sollte daher mit Feuer getötet werden. @terminus Dieser Link erwähnt nur die Existenz flexibler Array-Mitglieder in C99 (aber niemals C++); es unterstützt Ihre Behauptung nicht [0] ist eine gültige Syntax, um sie zu deklarieren, was sie nicht ist.

    – Unterstrich_d

    3. Juli 2016 um 5:09 Uhr


  • Arrays der Länge Null waren in keiner Version von C gültig. Sie können keine VLAs der Länge Null haben. Es ist eine gcc-Nicht-Standard-Erweiterung. Dieser Code wird weder in Standard-C noch in Standard-C++ kompiliert.

    – Ludin

    16. Dezember 2016 um 15:15 Uhr

Wenn Sie Ihre Anwendung darauf beschränken können, nur wenige bekannte Größen zu benötigen, können Sie mit einer Vorlage effektiv ein flexibles Array erreichen.

template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
    T flex_[SZ];
};

Wenn Sie nur wollen

struct blah { int foo[]; };

dann brauchen Sie die Struktur überhaupt nicht und können sich einfach mit einem malloc’ed/new’ed int-Array befassen.

Wenn Sie am Anfang einige Mitglieder haben:

struct blah { char a,b; /*int foo[]; //not valid in C++*/ };

dann in C++, ich nehme an, Sie könnten ersetzen foo mit einer foo Mitgliedsfunktion:

struct blah { alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); } };

Beispielanwendung:

#include <stdlib.h>
struct blah { 
    alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); }
};
int main()
{
    blah *b = (blah*)malloc(sizeof(blah)+10*sizeof(int));
    if(!b) return 1;
    b->foo()[1]=1;
}

Ein Vorschlag ist in Arbeit und könnte zu einer zukünftigen C++-Version führen. Sehen http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1039r0.html für Details (der Vorschlag ist ziemlich neu, daher sind Änderungen vorbehalten)

Sind flexible Array Member in C gultig
St. Antario

Ich hatte das gleiche Problem, um ein flexibles Array-Member zu deklarieren, das aus C++-Code verwendet werden kann. Indem man durchschaut glibc Header Ich habe festgestellt, dass es einige Verwendungen von flexiblen Array-Mitgliedern gibt, z. B. in struct inotify die wie folgt deklariert ist (Kommentare und einige nicht verwandte Mitglieder weggelassen):

struct inotify_event
{
  //Some members
  char name __flexarr;
};

Die __flexarr Makro wiederum ist definiert als

/* Support for flexible arrays.
   Headers that should use flexible arrays only if they're "real"
   (e.g. only if they won't affect sizeof()) should test
   #if __glibc_c99_flexarr_available.  */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif __GNUC_PREREQ (2,97)
/* GCC 2.97 supports C99 flexible array members as an extension,
   even when in C89 mode or compiling C++ (any version).  */
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif defined __GNUC__
/* Pre-2.97 GCC did not support C99 flexible arrays but did have
   an equivalent extension with slightly different notation.  */
# define __flexarr  [0]
# define __glibc_c99_flexarr_available 1
#else
/* Some other non-C99 compiler.  Approximate with [1].  */
# define __flexarr  [1]
# define __glibc_c99_flexarr_available 0
#endif

Ich kenne mich nicht aus MSVC Compiler, aber wahrscheinlich müssten Sie je nach ein weiteres bedingtes Makro hinzufügen MSVC Ausführung.

964060cookie-checkSind flexible Array-Member in C++ gültig?

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

Privacy policy