Ausrichtung in Strukturen



  • Ich hab noch immer nicht ganz durchschaut, wie man wie man wirklich portable Programme schreibt. Im Moment hab ich deshalb folgende Frage.

    Aus dem Lua-Buch habe ich eine Struktur, die für Lua ein Array von double werden soll:

    typedef struct NumArray {
      int size;
      double values[1];  /* variable part */
    } NumArray;
    

    Geplant ist da scheinbar, hinter dem values -Member genügend Platz für double's freizuhalten, damit auch Arrays mit mehr als einem Element möglich werden; und von values den Typ für Zeigeroperationen zu stehlen. Die sagen dazu:

    We declare the array values with size 1 only as a placeholder, because C does not allow an array with size 0; we will define the actual size by the space we allocate for the array. For an array with n elements, we need sizeof(NumArray) + (n-1)*sizeof(double) bytes. (We subtract one from n because the original structure already includes space for one element.)

    Weil diese Speicherbereiche unter dem Regime des Garbage-Collectors von Lua stehen, will man nur einen Bereich anfordern, und nicht noch einen zweiten, auf den ein Member von NumArray zeigt; wie ich das wohl unter anderen Umständen machen würde.

    Der Typ wird in dem Buch dann so verwendet, um ein neues Array zu erzeugen:

    // 'n' sei die gewünschte Grösse des Arrays
    size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);
    NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);
    

    Wobei wir für meine Zwecke annehmen können, dass lua_newuserdata(L, nbytes) ein anderer Ausdruck ist für: malloc(nbytes) . Das Ziel ist jedenfalls, dass nach diesen Zeilen a->values ein Zeiger auf einen Bereich ist, in den genau n double's passen.

    Nun meine Überlegungen zur Ausrichtung: in der NumArray -Struktur kommt zuerst ein int, danach ein double. Ob nach dem int Füllbytes sind, kann mir egal sein. Dass nach dem double noch Füllbytes sind, ist sehr unwahrscheinlich, weil double so gross ist. Also sollte die Vorgehensweise problemlos sein. Stimmt das?

    Und was wäre, wenn ich statt einem Array von double's ein Array von char's haben wollte? Wäre es da wahrscheinlicher, dass nach dem values -Member vom Typ char[1] noch Füllbytes sind? Macht es überhaupt Sinn, am Ende einer Struktur von Füllbytes zu sprechen?



  • ^^füllbytes kann es theoretisch immer geben, deshalb isses besser, structs immer als structs anzusprechen und nicht in char-arrays casten (und umgekehrt auch nicht).
    🙂



  • ;fricky schrieb:

    ^^füllbytes kann es theoretisch immer geben, deshalb isses besser, structs immer als structs anzusprechen und nicht in char-arrays casten (und umgekehrt auch nicht).
    🙂

    Daraus würde folgen, dass die Zeilen aus dem Buch nicht unbedingt portabel sind. Das wäre schade, weil mir sowas bei Lua sonst noch nicht untergekommen ist, obwohl ich schon den halben Quelltext kenne (viel ist es aber nicht). Das ist aber auch nur in dem Buch, vielleicht ist das ein nur ein böser Hack.

    However, ich brauche eine Möglichkeit, solche Dinge möglichst portabel hinzubekommen. Mir fällt dann nichts anderes mehr ein, als meine Werte byteweise zusammenzubauen. Geht das nicht komfortabler?

    Nochmals zusammengefasst: ich will einen Speicherbereich, in dem am Anfang ein int ist, dann können ruhig Füllbytes sein, und danach ein Array-artiger Bereich von Variablen eines bestimmten Typs.



  • Nachtrag: im Moment mache ich das so, dass ich aus dem int ein double gemacht hab, um die Ganzzahl für die Array-Grösse zu halten, dann kann ich auf die Struktur verzichten, und gleich ein double-Array nehmen. Geht in der Not auch. Aber zufrieden macht mich das nicht...



  • µngbd schrieb:

    Nun meine Überlegungen zur Ausrichtung: in der NumArray -Struktur kommt zuerst ein int, danach ein double. Ob nach dem int Füllbytes sind, kann mir egal sein. Dass nach dem double noch Füllbytes sind, ist sehr unwahrscheinlich, weil double so gross ist. Also sollte die Vorgehensweise problemlos sein. Stimmt das?

    Das hat mWn nichts mit der Größe von Double zu tun, sondern es wird sichergestellt, das die nächste Struktur oder sonstwas auch wieder am gewünschten Offset ausgerichtet ist. Ich vermute, das hast du auch so gemeint.

    µngbd schrieb:

    Und was wäre, wenn ich statt einem Array von double's ein Array von char's haben wollte? Wäre es da wahrscheinlicher, dass nach dem values -Member vom Typ char[1] noch Füllbytes sind?

    Kommt auf die Größe des Arrays an. Wenn damit die Größe der Struktur ein Vielfaches des Maschinenwortes ist, dann wird nix aufgefüllt, sonst ja.
    Im Falle von char[1] auf einer 32/64 Bit Maschine sehr wahrscheinlich, wenn du es nicht mit #pragma pack anders eingestellt hast.
    Kannst du leicht mit einer selbstgeschriebenen Struktur und dem sizeof Operator nachprüfen.

    µngbd schrieb:

    Macht es überhaupt Sinn, am Ende einer Struktur von Füllbytes zu sprechen?

    Ja, warum nicht? Durch das Anhängen der Füllbytes wird die Struktukr größer als sie es ohne die Füllbytes wäre.

    Gruß,
    B.B.



  • µngbd schrieb:

    ;fricky schrieb:

    ^^füllbytes kann es theoretisch immer geben, deshalb isses besser, structs immer als structs anzusprechen und nicht in char-arrays casten (und umgekehrt auch nicht).
    🙂

    Daraus würde folgen, dass die Zeilen aus dem Buch nicht unbedingt portabel sind.

    das mit dem 'numarray' da oben^^ ist doch portabel. wenn ein compiler füllbytes reinstopft, geht's trotzdem.
    beispiel:

    typedef struct NumArray 
    {
      char fuell2[17];       // <--- kuenstliche fuellbytes 
      int size;
      char fuellbytes[57];   // <--- kuenstliche fuellbytes
      double values[1];
      char fuell3[93];       // <--- kuenstliche fuellbytes
    } NumArray;
    
    NumArray *create (int n)
    {
      NumArray *p = malloc (sizeof(*p) + (n-1) * sizeof(p->values)); 
      if (p)
      {
        p->size = n;
    
        // test: auffuellen mit 0...n-1
        {
          int s;
          for (s=0; s<n; s++)
            p->values[s] = s;
        }
      }
      return p;
    }
    
    int main()
    {
      int s;
    
      NumArray *p = create (10);    // <--- 10 elemente
    
      if (p)
      {
        for (s=0; s<p->size; s++)
          printf ("%f\n", p->values[s]);   // <--- alles ausgeben
    
        free (p);
      }
    }
    


  • ;fricky schrieb:

    ...
      NumArray *p = malloc (sizeof(*p) + (n-1) * sizeof([b]p->values[/b])); 
    ...
    

    edit: sollte besser 'p->values[0]' sein.
    🙂



  • ;fricky schrieb:

    ;fricky schrieb:

    ...
      NumArray *p = malloc (sizeof(*p) + [b](n-1)[/b] * sizeof([b]p->values[/b])); 
    ...
    

    edit: sollte besser 'p->values[0]' sein.
    🙂

    Nur so überflogen ... sicher nicht eher n?



  • pointercrash() schrieb:

    ... sicher nicht eher n?

    nee, 'sizeof (NumArray)' beinhaltet doch schon ein double.
    🙂



  • Ich hab in Ruhe drüber nachgedacht:

    fricky schrieb:

    edit: sollte besser 'p->values[0]' sein.

    Schon klar, verstehe was gemeint ist.

    Big Brother schrieb:

    Kannst du leicht mit einer selbstgeschriebenen Struktur und dem sizeof Operator nachprüfen.

    Ja, sicher. Nur hatte ich Zweifel, ob das auf allen Maschinen gut gehen wird. Mir wird das immer klarer.

    fricky schrieb:

    das mit dem 'numarray' da oben^^ ist doch portabel. wenn ein compiler füllbytes reinstopft, geht's trotzdem.

    Da hast du eigentlich recht. Dein Beispiel ist zweifellos portabel. Eigentlich verwendet man dort (unter üblichen Umständen) für die zehn double's den Speicher aus fuell3[93] , oder?

    Was mich da so verwirrt hat? Ich hab überlegt, was passiert, wenn ich aus dem double ein char mache, also ein "CharArray" anlege:

    typedef struct CharArray
    {
      int size;         /* nehmen wir mal an, das sind 4 Bytes */
      char values[1];   /* und weiters, dass das hier noch eines ist, macht 5 */
      /* und dass hier noch 3 Füllbytes gemacht werden, macht 8 in Summe */
    } CharArray;
    
    /* jetzt will ich ein CharArray von n=10 char's machen: */
    CharArray *p = malloc(sizeof(*p) + (n-1) * sizeof(p->values[0]));
    /* ergibt dann also: */
                   malloc(8          + 9     * 1);
    

    Dann ist eigentlich nichts schlimmes passiert, ausser dass ich 3 Bytes zuviel angefordert habe, die dann am Ende des Bereichs liegen und nicht verwendet werden? Damit könnte ich natürlich problemlos leben. Hab ich da recht?

    Was man mit C nicht für seltsame Sachen machen kann...
    🙂



  • µngbd schrieb:

    Dein Beispiel ist zweifellos portabel. Eigentlich verwendet man dort (unter üblichen Umständen) für die zehn double's den Speicher aus fuell3[93] , oder?

    nö, dieses 'füll3' und die anderen fülls auch, darfste natürlich nicht verwenden, die existieren in wirklichkeit ja auch nicht als struct members, die man ansprechen kann. durch das malloc (sizeof(struct blah) + irgendwas) wird die so erzeugte struct-instanz nach hinten künstlich vergrössert, so dass nur der letzte member grösser wird. und wenn er ein array ist, kannste die einzelnen elemente über [] adressieren. das ist eigentlich alles, keine hexerei. beim gcc kannst du übrigens ...

    struct
    {
      ...
      typ array[b][0][/b];
    };
    

    ... machen, und sparst damit die -1 im malloc-aufruf. ist aber nach ansi-C, glaub ich, nicht erlaubt.

    µngbd schrieb:

    Was mich da so verwirrt hat? Ich hab überlegt, was passiert, wenn ich aus dem double ein char mache, also ein "CharArray" anlege.

    geht analog dazu. das array (ganz hinten!) kann irgendein typ sein, ist völlig egal.

    µngbd schrieb:

    Was man mit C nicht für seltsame Sachen machen kann...

    mein reden. C ist irgendwie einfach und genial zugleich. so mancher noob würde gern ein ++ dranklatschen, aber ihm sei gesagt: 'C++ makes C bigger but returns the old value' (wenn überhaupt. sorry, volkard).
    🙂


Anmelden zum Antworten