Uninitialisiertes Array in eigenem Struct mit unbestimmter Grösse.



  • Hi all,

    mal ne Frage zu meinem Struct:

    typedef Mein_Struct {
        int size;
        char array[size];
        /*
        ...
        */
    } my_struct;
    

    Das Struct ist zu beginn uninitialisiert, trotzdem compiliert das so nicht,
    weil "size" undefiniert (weil uninitialisiert) ist.

    Meine (falsche) Annahme war, ich schreibe eine Prozedur set_size(int s), welches einen uninitialisierten Prototypen des Structs nimmt und dann einfach size setzt, somit auch die max. Stringgrösse vom array. Hier pseudo-Code, wie es gemeint ist:

    void set_size(my_struct *m, int s) {
        m->size=s;
    }
    
    /* ... */
    
    my_struct m; // leeres Mein_Struct
    set_size(&m, 32);
    /* XYZ */
    

    Ich dachte ab der Markierung XYZ ist m->size=32 UND m->array 32 Elemente gross.

    Das stimmt offenbar NICHT.

    Also wie bekomme ich ein Struct, mit einem Array unbestimmter Grösse darin?
    (bzw. zum Zeitpunkt der Definition des Structs ist die Grösse unbekannt, muss/soll später gesetzt werden über Prozeduren/Funktionen.)

    Oder ist die Frage an sich schon Käse?
    (Kommt bei mir gerne mal vor, schliesslich habe ich das Programmieren mit anderen Sprachen gelernt...)
    Wenn ja, bitte workaround beschreiben, wie man das in ANSI C i.d.R. so macht/löst.

    Danke wie immer im Voraus.



  • Du hast einen Fehler in der Struct-Definition gemacht.

    typedef [b]struct[/b] {
    
    ...
    
    } mystruct;
    
    ...
       char array[size];
    ...
    

    kann man nur dann im einem Struct verwenden, wenn size zur Kompilierzeit
    bekannt ist, so z.B.:

    #define SIZE 100
    typedef struct {
       int  size;
       char array[SIZE];
    } mystruct;
    

    Dies hat den Nachteil, dass du nur SIZE-viele Elemente in den Array speichern
    kannst und dass immer Speicherplatz für SIZE-viele Elemente angelegt wird,
    auch wenn du nur ein Element einfügst.

    Variabler kannst du das hier mit Pointern machen:

    typedef struct {
       int  size;
       char *array;
    } mystruct;
    
    mystruct *new_mystruct(int size) {
       mystruct *el = malloc(sizeof(*el));
       el->size  = size;
       if (size>0) el->array = malloc(size*sizeof(*(el->array)));
       else el->array = NULL;
       return el;
    }
    

    Gruß mcr



  • Seit C99 gibt es flexible array members:

    #include <stdlib.h>
    
    struct foo {
        size_t size;
        char fam[]; // muss letztes Element in Struct sein
    };
    
    int main (int argc, char **argv) {
        struct foo *bar = malloc (sizeof *bar + argc);
        bar->size = argc;
        free (bar);
        return 0;
    }
    


  • Tim schrieb:

    Seit C99 gibt es flexible array members:

    #include <stdlib.h>
    
    struct foo {
        size_t size;
        char fam[]; // muss letztes Element in Struct sein
    };
    
    int main (int argc, char **argv) {
        struct foo *bar = malloc (sizeof *bar + argc);
        bar->size = argc;
        free (bar);
        return 0;
    }
    

    Meines Wissens nach ist im Standard nicht festgelegt, dass die Reihenfolge
    der Members eines Structs vom Compiler nicht verändert werden darf.
    Das würde bedeuten, dass fam nicht immer an letzter Stelle sein muss.
    Ob sich das nun mit C99 und flexiblen array members geändert hat, weiß ich
    nicht.
    Bisher ist mir auch kein Compiler bekannt, der die Reihenfolge ändert.

    Dein Beispiel funktioniert so auch vor C99 (ausgenommen s.o.):

    struct foo {
       int size;
       char fam[0];
    }
    

    Sobald du aber einen anderen Typ als char verwendest, funktioniert die
    Allozierung nicht mehr. Dann müsste es so lauten:

    struct foo {
       int size;
       int fam[0];
    }
    
    struct foo *bar = malloc(sizeof(*bar) + (size-1) * sizeof(*(bar->fam)));
    bar->size = size;
    

    Der Hack, den sich Tim hier zu Hilfe macht:
    C verhindert nicht den Zugriff auf Speicher (Elemente) über Arraygrenzen
    hinaus.

    Mit malloc wird genügend Speicher alloziert, auf dem dann mit fam[i]
    zugegriffen werden kann.

    Ein Beispiel:

    Startadresse von bar:              0x00  (ich weiß, dass das keine gültige Adresse ist.)
    Dann beginnt das Array fam bei:    0x04
    Ein Malloc mit size=4 reserviert 8 (sizeof(*bar)) + 3*4 (size-1 * sizeof(int))
    also 20 bytes, die wie folgt verteilt sind:
    
    bar->size:   0x00 - 0x03
    bar->fam:    0x04 - 0x13
    bar->fam[0]:              0x04 - 0x07
    bar->fam[1]:              0x08 - 0x0b
    bar->fam[2]:              0x0c - 0x0f
    bar->fam[3]:              0x10 - 0x13
    

    Diese Vorgehensweise hat als einzigen Vorteil, dass der Speicher für einen
    Pointer gespart wird und die Daten hintereinander im Speicher liegen.

    Welche Variante nun die bessere, oder sichere ist, bleib dem Programmierer
    überlassen.

    Gruß mcr

    EDIT:
    aber bei allen Möglichkeiten wird das Array nicht automatisch angelegt,
    sondern muss handisch mit malloc alloziert werden.



  • mcr schrieb:

    Meines Wissens nach ist im Standard nicht festgelegt, dass die Reihenfolge
    der Members eines Structs vom Compiler nicht verändert werden darf.
    Das würde bedeuten, dass fam nicht immer an letzter Stelle sein muss.
    Ob sich das nun mit C99 und flexiblen array members geändert hat, weiß ich
    nicht.

    Ich sehe den Widerspruch nicht. Ein flexible array member muss das letze Element sein. Innendrin darf die Reihenfolge (das erste Element und das optionale fam) sicherlich durchgewürfelt werden.

    mcr schrieb:

    Dein Beispiel funktioniert so auch vor C99 (ausgenommen s.o.):

    struct foo {
       int size;
       char fam[0];
    }
    

    Das ist zwar mit ein paar Compilern möglich (gcc z.B.), ist aber non-standard.

    mcr schrieb:

    Der Hack, den sich Tim hier zu Hilfe macht:
    C verhindert nicht den Zugriff auf Speicher (Elemente) über Arraygrenzen
    hinaus.

    Ein Hack der so im Standard als Beispiel steht...



  • Tim schrieb:

    Innendrin darf die Reihenfolge (das erste Element und das optionale fam) sicherlich durchgewürfelt werden.

    werden sie nicht. struct members werden in der reihenfolge gespeichert, in der man sie hingeschrieben hat. irgendwie gab's, glaub ich, 'ne ausnahme bei bitfields.
    🙂



  • ~fricky schrieb:

    Tim schrieb:

    Innendrin darf die Reihenfolge (das erste Element und das optionale fam) sicherlich durchgewürfelt werden.

    werden sie nicht. struct members werden in der reihenfolge gespeichert, in der man sie hingeschrieben hat. irgendwie gab's, glaub ich, 'ne ausnahme bei bitfields.
    🙂

    Tatsache. Ich weiss gar nicht wo ich das her hab 😕



  • Tim schrieb:

    Tatsache. Ich weiss gar nicht wo ich das her hab 😕

    Vielleicht von hier ?
    Guckst du:
    http://www.comeaucomputing.com/techtalk/c99/#flexiblearrays

    Gruß,
    B.B.



  • B.B. schrieb:

    Tim schrieb:

    Tatsache. Ich weiss gar nicht wo ich das her hab 😕

    Vielleicht von hier ?
    Guckst du:
    http://www.comeaucomputing.com/techtalk/c99/#flexiblearrays

    Es ging ja eher darum ob die Reihenfolge der Elemente eines struct die Reihenfolge der Deklaration einhalten müssen oder nicht. Oder verstehe ich dich falsch?



  • Ich dachte du wüsstest nicht mehr, woher du das Wissen um die letzte Position eines flexible array members hast.

    Zur Reihenfolge der struct member sagt Committee Draft — May 6, 2005

    6.7.2.1.
    12 Each non-bit-field member of a structure or union object is aligned in an implementationdefined
    manner appropriate to its type.



  • Das bezieht sich auf das alignment der Elemente (also wo padding bytes eingefügt werden), nicht auf die Reihenfolge der Elemente selbst.



  • Nagut, dann hab ich da wohl was mistverstanden.


Anmelden zum Antworten