Zero-size Array in struct



  • Hallo,

    ich habe mal ein kleines Beispiel um mein Problem zu verdeutlichen:

    typedef struct SubLists {
    	unsigned int id;
    	char name[14];
    } SubLists;
    
    typedef struct LIST {
    	unsigned short ListeType;
    	unsigned short ListLength;
    	int id;
    	char name[8];
    	SubLists subLists[];
    }LIST;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	char msg[] = { 0x12, 0x00, 0x34, 0x00, 0xA8, 0x22, 0x00, 0x00, 0x54, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x23, 0x00, 0x00, 0x47, 0x72, 0x75, 0x70, 0x70, 0x65, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x27, 0x00, 0x00, 0x47, 0x72, 0x75, 0x70, 0x70, 0x65, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    	auto liste = *(struct LIST*)msg;
    
    	std::cout << "Name: " << liste.name << "\n"; // <- Alles richtig
    
    	std::cout << "SubName: " << liste.subLists[0].name << "\n"; // <- Nicht richtig :(
    };
    

    ich habe einen char array den ich in ein Struct packe. Das ging bis jetzt immer. Nun möchte ich aber ein Struct in einem Struct packen wobei die Anzahl nicht bekannt ist. Was aber bekannt ist, ist die länge des char array. Wie bekomme ich die Informationen von char array in SubLists ?



  • Möglicherweise liegt es am Inhalt deines msg-Arrays?



  • zero-sized arrays != flexible array members?
    Beides in C++ illegal?



  • Da wir hier im C++-Bereich sind sage ich einfach mal std::vector



  • Leider ist der char array vorgegeben, also diesen empfange ich so per Netzwerk. Mit Hilfe von struct's lassen sich die Daten danach recht einfach verarbeiten ohne großen Aufwand.



  • "Nicht richtig" - das beschreibt es ja sehr genau.
    Wer weiß, ob das array überhaupt korrekte Daten enthält.



  • In dem Fall ist das ein von mir erzeugtes Array zum testen und da müsste GruppaA und GruppeB stehen.



  • auto liste = *(struct LIST*)msg;
    

    muss natürlich

    auto liste = (struct LIST*)msg;
    

    sein und entsprechend mit pointern gearbeitet werden.



  • Und statt char muss msg unsigned char sein.



  • Danke dir, nun geht es 🙂



  • MrSpoocy schrieb:

    Danke dir, nun geht es 🙂

    Und verstehst du auch, warum?



  • Ehrlich gesagt nicht ganz, ich dachte mir schon das SubListe ein Pointer sein wird und habe wilde dinge wie

    auto sublist = (struct SubLists)&liste.subLists[0];

    versucht. Aber alles wollte nicht so ganz klappen.



  • Dein Problem war, dass du in der Zeile

    auto liste = *(struct LIST*)msg;
    

    eine Kopie deines Structs machst, da du nach dem Cast dereferenzierst. Der Compiler muss also dein Objekt kopieren, aber da du Zero Length Arrays in deinem Struct hast (übrigens kein Feature, dass es laut Standard gibt) kann der Compiler überhaupt nicht wissen wie viele Bytes er kopieren muss. Deine subList enthält also Müll. Wenn du aber in dieser Zeile nicht dereferenzierst speicherst du dir nur einen Pointer und das ist OK.

    Die unsigned char Geschichte ist übrigens nicht notwendig. Ohne gibts zwar ein paar Warnungen, aber die Werte werden trotzdem richtig konvertiert.

    Irgendwie ist dein Code auch ein sehr merkwürdiger Mix zwischen C und C++. Erstmal ist das Konstrukt typedef struct {/* stuff */} LIST; typisch C, um nicht überall ein struct vor LIST schreiben zu müssen. Du schreibst es aber trotzdem noch davor. In C++ ist weder das typedef noch struct überall nötig. Außerdem sollte man sich angewöhnen einen der C++ Casts zu nutzen, statt den C Cast, der einfach alles castet was man ihm vorwirft. So würde dann die C++ Variante deines Codes aussehen:

    struct SubLists {
      unsigned int id;
      char name[14];
    };
    
    struct LIST {
      unsigned short ListeType;
      unsigned short ListLength;
      int id;
      char name[8];
      SubLists subLists[];
    };
    
    int main()
    {
      unsigned char msg[] = { 0x12, 0x00, 0x34, 0x00, 0xA8, 0x22, 0x00, 0x00, 0x54, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x23, 0x00, 0x00, 0x47, 0x72, 0x75, 0x70, 0x70, 0x65, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x27, 0x00, 0x00, 0x47, 0x72, 0x75, 0x70, 0x70, 0x65, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
      auto liste = reinterpret_cast<LIST*>(msg);
    
      std::cout << "Name: " << liste->name << "\n";
      std::cout << "SubName: " << liste->subLists[0].name << "\n";
    }
    


  • MrSpoocy schrieb:

    Ehrlich gesagt nicht ganz, ich dachte mir schon das SubListe ein Pointer sein wird und habe wilde dinge wie

    auto sublist = (struct SubLists)&liste.subLists[0];

    versucht. Aber alles wollte nicht so ganz klappen.

    Wie wäre es denn die subLists aus dem LIST -Struct herauszunemen und einfach zwei Pointer zu verwenden. Z.B. Einen ListHead* , den du auf den Anfang deines msg -Arrays abbildest, und ein SubLists* der auf den Unterlisten-Teil des msg -Arrays zeigt, sofern dieser vorhanden ist (ich vermute mal wenn ListLength > 0 , oder?). Wenn keine Sublists vorhanden sind, dann kann ist der Sublists-Pointer eben nullptr . Die beiden Pointer kann man dann auch wieder schn in einer "eigentlichen" List -Klasse zusammenfassen und so die innereien wegabstrahieren. Ungefähr so oder ähnlich:

    struct List
    {
        struct SubList {
            unsigned int id;
            char name[14];
        };
    
        struct Head {
            unsigned short ListeType;
            unsigned short ListLength;
            int id;
            char name[8];
        };
    
        Head* head;
        SubList* subLists;
    
        inline List(char* msg)
        :   head(reinterpret_cast<Head*>(msg)),
            subList(nullptr)
        {
            if (head != nullptr && head->ListLength > 0)
                subLists = reinterpret_cast<SubList*>(msg + sizeof(head));
        }
    };
    

    Instanzen von List , bzw. deren Unterklassen sind natürlich auch hier nur über msg "drübergelegt", d.h. msg sollte mindestens so lange leben, wie es eine List -Instanz tut.

    Finnegan

    P.S.: Mit meinem Ansatz Versuche ich das meines erachtens hauptsächliche Problem zu lösen, dass Struct-Member-Arrays eine feste Länge > 0 haben müssen, die scheinbar anderen Probleme die es da noch gab/gibt hab ich mir nicht genau ansgesehen 😃
    P.P.S: Beim "draufmappen" von Structs auf irgendwelche Byte-Daten muss man natürlich eventuell noch eine Reihe Details beachten, z.B. dass das Struct auch wirklich "passt und bündig ist", falls der Compiler meint da z.B. noch irgendwelches Padding und Alignment machen zu müssen. Eventuell benötigt man da noch irgenwelche Compiler-spezifischen Sachen ( #pragma pack() et al.). Auch muss man eventuell bei so einem unsigned int in dem Struct die Endianess anpassen, je nachdem was da für Daten reinkommen (vielleicht von einem System mit anderer Endianess ohne dass das Protokoll sich auf eine von beiden festlegt).


Log in to reply