Marcel: C - Pointer-Arithmetik Problem

Hallo

Ich alloziere mit Hilfe von malloc() Speicher. Malloc() gibt mir den Pointer zum allozierten Speicher zurück.
Nun möchte ich den reservierten Speicher in Blocks aufteilen, sodass ich den ersten Block z.B. für ein Integer-Flag, den zweiten Block für eine Pointer-Adresse und den dritten Block für andere Daten nutzen oder noch weiter aufteilen kann.

Wichtig ist, dass ich später von dem am Anfang von malloc() erhaltenen Pointer wieder zum Integer-Flag oder zum zweiten Block finde (Die grössen sind bekannt).

Ich hab sowas versucht:

  
void* area = malloc( 64*1024*1024 );  
  
//Schreibe 1. Block Integer-Flag  
int flag = 1;  
area = &type;  

Wie kann ich nun in einen 2. Block einen Pointer platzieren?

Wie erhalte ich den Wert des flags (0 oder 1) wieder wenn ich den Pointer zu area gegeben habe?

Gruss Marcel

  1. Hi Marcel!

    Nun möchte ich den reservierten Speicher in Blocks aufteilen, sodass ich den ersten Block z.B. für ein Integer-Flag, den zweiten Block für eine Pointer-Adresse und den dritten Block für andere Daten nutzen oder noch weiter aufteilen kann.

    Wieso arbeitest du dann nicht mit einer solchen Struktur?

    Wichtig ist, dass ich später von dem am Anfang von malloc() erhaltenen Pointer wieder zum Integer-Flag oder zum zweiten Block finde (Die grössen sind bekannt).

    Dann könntest du auch entsprechenden Speicherplatz allokieren und dir den Zeiger auf eine solche Struktur zurückgeben lassen.

    Wie kann ich nun in einen 2. Block einen Pointer platzieren?

    Es ist in C durchaus möglich Zeiger zu inkrementieren bzw. bitweise zu erhöhen. [1]

    Wie erhalte ich den Wert des flags (0 oder 1) wieder wenn ich den Pointer zu area gegeben habe?

    Du machst es dir einfach zu schwer. Wie um alles in der Welt willst du darauf zugreifen? Du müsstest dir alles auseinanderfriemeln. Mach es dir nicht so schwer und arbeite mit einer ordentlichen und sauberen Struktur.

    [1] Nagel mich da nicht fest. Ich habe das noch nicht selbst angewandt und glaube, es gelesen zu haben.

    MfG H☼psel

    --
    "It's amazing I won. I was running against peace, prosperity, and incumbency."
    George W. Bush speaking to Swedish Prime Minister unaware a live television camera was still rolling, June 14, 2001
    Selfcode: ie:% fl:( br:> va:) ls:& fo:) rl:? n4:& ss:| de:] js:| ch:? sh:( mo:) zu:)
    1. Hallo Hopsel,

      Wieso arbeitest du dann nicht mit einer solchen Struktur?

      ... und einem typisierten Zeiger auf diese Struktur anstatt einem void* ?
      Die Frage lag mir auch auf der Zunge.

      Es ist in C durchaus möglich Zeiger zu inkrementieren bzw. bitweise zu erhöhen. [1]

      Nein, nicht bitweise. C kann als kleinste Einheit ein Byte (char) adressieren. Einzelne Bits kriegt man daraus durch Maskierungsoperationen (logisches AND/OR/XOR).
      Ja, ich weiß: In structs kann man Felder definieren, die kleiner als ein Byte sind und nicht einmal an Bbytegrenzen beginnen bzw. enden müssen:

      typedef struct
       { int status:3;
         int command:3;
         int count:12;
       } CTRLWORD;

      Damit deklariere ich eine gepackte Struktur von insgesamt 16bit (1 WORD) Länge, wovon ich die untersten 3bit als status, die nächsten 3bit als command und die restlichen 12bit als count ansprechen kann, ohne im Sourcecode noch mit aufwendigen Bitoperation jonglieren zu müssen.
      Das ist aber AFAIK in C das einzige Zugeständnis an Bit-Adressierung, und es wird vom Compiler auch wieder in eine Kombination aus BYTE/WORD/DWORD-Zugriff und Maskierung übersetzt. Man könnte das also auch "zu Fuß" programmieren, aber so ist es komfortabler.

      Schönen Abend noch,

      Martin

      --
      Schildkröten können mehr über den Weg berichten als Hasen.
      1. Hi Der!

        Wieso arbeitest du dann nicht mit einer solchen Struktur?
        ... und einem typisierten Zeiger auf diese Struktur anstatt einem void* ?

        Das ist m.E. selbstverständlich.

        Es ist in C durchaus möglich Zeiger zu inkrementieren bzw. bitweise zu erhöhen. [1]
        Nein, nicht bitweise. C kann als kleinste Einheit ein Byte (char) adressieren.

        Ja, das dachte ich mir schon. Nichtsdestotrotz sind die einzelnen Bits eines Bytes einfacher zu verwalten, als die eines 64bit-Wortes. :)

        Ja, ich weiß: In structs kann man Felder definieren, die kleiner als ein Byte sind und nicht einmal an Bytegrenzen beginnen bzw. enden müssen:
        typedef struct
        { int status:3;
           int command:3;
           int count:12;
        } CTRLWORD;

        Das ist mir vollkommen neu. Wie setzt der Compiler das denn um? Gehört es zum ANSI-C-Standard?

        Damit deklariere ich eine gepackte Struktur von insgesamt 16bit (1 WORD) Länge [...]

        12+3+3 sind doch 18. Hast du einen Fehler gemacht oder habe ich das falsch verstanden?

        MfG H☼psel

        --
        "It's amazing I won. I was running against peace, prosperity, and incumbency."
        George W. Bush speaking to Swedish Prime Minister unaware a live television camera was still rolling, June 14, 2001
        Selfcode: ie:% fl:( br:> va:) ls:& fo:) rl:? n4:& ss:| de:] js:| ch:? sh:( mo:) zu:)
        1. Moin!

          Ja, ich weiß: In structs kann man Felder definieren, die kleiner als ein Byte sind und nicht einmal an Bytegrenzen beginnen bzw. enden müssen:
          typedef struct
          { int status:3;
             int command:3;
             int count:12;
          } CTRLWORD;

          Das ist mir vollkommen neu. Wie setzt der Compiler das denn um? Gehört es zum ANSI-C-Standard?

          Soweit ich weiß, ja. Aber ich bin mir nicht sicher. Die Microsoft- und Borland-Compiler kennen das jedenfalls.
          Wie er das umsetzt? Na, für die Speicherreservierung ist das ja kein Problem. Der Speicherbedarf wird dabei einfach auf den nächsten möglichen Bytewert (oder WORD, oder DWORD, je nach Architektur) aufgerundet. Und der Zugriff auf diese Felder wird im Quellcode genauso formuliert wie bei einer herkommlichen Struktur, nur dass z.B. command nur einen Wertebereich von 0..7 (oder -4..+3) hat.
          Formuliere ich einen lesenden Zugriff auf ctrlword.command, dann codiert der Compiler einen BYTE/WORD/DWORD-Zugriff auf die gesamte Struktur mit einem anschließenden >>3 und &7.

          12+3+3 sind doch 18.

          Hmm, jetzt wo du das so sagst... ;-)
          Stimmt natürlich, dann ist das Konstrukt, das ich als Beispiel konstruiert habe, natürlich 18bit lang, und der Compiler wird es vermutlich auf 32bit aufrunden.

          Schönen Tag noch,

          Martin

          --
          Du kannst dem Leben nicht mehr Tage geben.
          Aber dem Tag mehr Leben.
          1. Moin,

            Ja, ich weiß: In structs kann man Felder definieren, die kleiner als ein Byte sind und nicht einmal an Bytegrenzen beginnen bzw. enden müssen:
            typedef struct
            { int status:3;
               int command:3;
               int count:12;
            } CTRLWORD;

            Das ist mir vollkommen neu. Wie setzt der Compiler das denn um? Gehört es zum ANSI-C-Standard?

            Soweit ich weiß, ja. Aber ich bin mir nicht sicher. Die Microsoft- und Borland-Compiler kennen das jedenfalls.

            Ja, das ist ANSI-C

            Wie er das umsetzt? Na, für die Speicherreservierung ist das ja kein Problem. Der Speicherbedarf wird dabei einfach auf den nächsten möglichen Bytewert (oder WORD, oder DWORD, je nach Architektur) aufgerundet. Und der Zugriff auf diese Felder wird im Quellcode genauso formuliert wie bei einer herkommlichen Struktur, nur dass z.B. command nur einen Wertebereich von 0..7 (oder -4..+3) hat.
            Formuliere ich einen lesenden Zugriff auf ctrlword.command, dann codiert der Compiler einen BYTE/WORD/DWORD-Zugriff auf die gesamte Struktur mit einem anschließenden >>3 und &7.

            12+3+3 sind doch 18.

            Hmm, jetzt wo du das so sagst... ;-)
            Stimmt natürlich, dann ist das Konstrukt, das ich als Beispiel konstruiert habe, natürlich 18bit lang, und der Compiler wird es vermutlich auf 32bit aufrunden.

            Wie der Compiler diese Daten ablegt, ist seine Sache. Insbesondere sollte man nicht durch das Addieren von Bytes meinen, die Größe einer Struktur zu kennen, da Strukturen (und auch die Daten innerhalb einer Struktur) gerne an Grenzen ausgerichtet werden, die für einen schnellen Zugriff günstig sind.

            Bei die 16-Bit Prozessoren ist es oft effizienter Strukturen und auch Daten innerhalb einer Struktur an einer geraden Adresse beginnen zu lassen. Für die 32-Bitter gilt ähnliches für durch 4 teilbare Adressen. Entsprechendes gilt für andere Architekturen.

            @Marcel: Nimm eine anständige Struktur und arbeite mit sizeof bzw. Pointerarithmetik bzw. dem []-Operator. Oder gleich C++ (mit new[] und delete[]), falls dies möglich ist.

            Claus

            1. Hallo Claus,

              Wie der Compiler diese Daten ablegt, ist seine Sache.

              das will ich nicht hoffen. Schließlich muss die Struktur-Deklaration beispielsweise zu einem Hardware-Interface (z.B. einem memory-mapped I/O-Port) passen, wenn ich auf Hardware- und Treiberebene arbeite. Denn das ist wohl der hauptsächliche Anwendungsbereich für solche Bit-Felder.

              Insbesondere sollte man nicht durch das Addieren von Bytes meinen, die Größe einer Struktur zu kennen, da Strukturen (und auch die Daten innerhalb einer Struktur) gerne an Grenzen ausgerichtet werden, die für einen schnellen Zugriff günstig sind.

              Wenn ich einen modernen Compiler entsprechend dazu anweise, tut er das tatsächlich. Aber ebenso kann ich ihn durch geeignete Direktiven anweisen, auf dieses Alignment zu verzichten und stattdessen alles dicht an dicht zu packen. Das ist meine bevorzugte Einstellung, weil ich normalerweise maximale Kontrolle über den Programmcode haben will. Wenn ich die Daten wirklich zugriffsoptimiert ausrichten will, mache ich das an der Stelle, wo es sein soll, explizit (notfalls, wenn der Compiler das nicht besser kann, wieder durch Auszählen und Auffüllen mit Dummy-Bytes).

              Bei die 16-Bit Prozessoren ist es oft effizienter Strukturen und auch Daten innerhalb einer Struktur an einer geraden Adresse beginnen zu lassen. Für die 32-Bitter gilt ähnliches für durch 4 teilbare Adressen. Entsprechendes gilt für andere Architekturen.

              ACK. Wobei das bei den meisten CPUs nur dann eine Rolle spielt, wenn bei einem Zugriff eine entsprechende z.B. durch 4 teilbare Adresse _überschritten_ wird. Liegt ein kleineres Element (z.B. WORD) _innerhalb_ eines DWORD-optimierten Adressfensters, hat das normalerweise keine Nachteile, auch wenn dieses Element nicht exakt auf eine durch 4 teilbare Adresse ausgerichtet ist.

              Schönes Wochenende,

              Martin

              --
              Das Gehirn ist schon eine tolle Sache: Es fängt ganz von allein an zu arbeiten, wenn man morgens aufsteht, und hört erst damit auf, wenn man in der Schule ankommt.
                (alte Schülererkenntnis)
      2. Hallo

        typedef struct
        { int status:3;
           int command:3;
           int count:12;
        } CTRLWORD;

        Damit deklariere ich eine gepackte Struktur von insgesamt 16bit (1 WORD) Länge, wovon ich die untersten 3bit als status, die nächsten 3bit als command und die restlichen 12bit als count ansprechen kann, ohne im Sourcecode noch mit aufwendigen Bitoperation jonglieren zu müssen.
        Das ist aber AFAIK in C das einzige Zugeständnis an Bit-Adressierung, und es wird vom Compiler auch wieder in eine Kombination aus BYTE/WORD/DWORD-Zugriff und Maskierung übersetzt. Man könnte das also auch "zu Fuß" programmieren, aber so ist es komfortabler.

        Mhmmm...wenn ich nun den Pointer *p welcher auf einen Block von 64 MB Grösse zeigt erhalte, wie kann ich dann die ein solches Struct z.B. am Anfang des Blocks platzieren und dann z.B. in der Mitte des Speichers nochmals...?

        Gruss Marcel

        1. Hallo Marcel,

          Mhmmm...wenn ich nun den Pointer *p welcher auf einen Block von 64 MB Grösse zeigt erhalte, wie kann ich dann die ein solches Struct z.B. am Anfang des Blocks platzieren und dann z.B. in der Mitte des Speichers nochmals...?

          also ein Beispiel. Nehmen wir mal an, du hast eine x-beliebige Struktur deklariert:

          typedef struct
           { // Aufbau der Struktur belanglos
           } ANYSTRUCT;

          Dann reservierst du einen üppigen Speicherblock:

          ANYSTRUCT *p;
          p = malloc(12000*sizeof(ANYSTRUCT));

          Jetzt solltest du noch prüfen, ob die Reservierung vielleicht fehlgeschlagen ist (dann wäre p==NULL), und wann das in Ordnungist, kannst du mit dem Basiszeiger p umgehen, als wäre er ein Array, weil Arrays und Zeiger in C in den meisten Kontexten gleichwertig sind:

          p[17] = p[0];

          Wenn die Indizierung nicht nach deinem Geschmack ist, kannst du auch explizit mit Zeigern hantieren:

          ANYSTRUCT *p1, *p2;
          p1 = p+120;     // p1 zeigt jetzt auf p[120]
          p2 = p+700;     // p2 zeigt jetzt auf p[700]
          p1->feld1 = 1;  // Zugriff auf struct-Elemente über den Zeiger
          p1->feld2 = 4;  // Zugriff auf struct-Elemente über den Zeiger
          *p2 = *p1;      // kopiere das Element, auf das p1 zeigt, nach p2
          p1++;           // p1 zeigt _jetzt_ auf p[121]

          Du siehst also, dass man auch mit Zeigern recht komfortabel arbeiten kann, wenn man ihnen einen Typ verpasst, anstatt void* zu deklarieren. Vielleicht sieht das am Anfang alles noch ein bisschen kryptisch aus, aber Übung macht den Meister.

          Schönes Wochenende schon mal,

          Martin

          --
          Paradox ist, wenn der Innenminister sich äußert und der Außenminister sich erinnert.
          1. Hi Martin!

            kannst du mit dem Basiszeiger p umgehen, als wäre er ein Array, weil Arrays und Zeiger in C in den meisten Kontexten gleichwertig sind:

            Ich habe noch eine Bemerkung, die du mir noch bestätigen könntest.

            Wenn du mit Hilfe einer n Byte großen Struktur Speicherplatz allokierst - genauso wie du es getan hast ([1]) - Dann verschiebt der Compiler bei einer Zuweisung wie p1 = p+120; den Zeiger p1 um p + 120*n Byte.
            Was ich wissen möchte ist, ob der Compiler bei p++; nicht um 1 Byte oder Bit erhöht, sondern um die Größe der Struktur auf die p zeigt.

            Ich hoffe, ich konnte mich verständlich ausdrücken.

            [1] p = malloc(1000*n);
            Wobei p vom Typ Zeiger auf die n-byte-große Struktur ist.

            MfG H☼psel

            --
            "It's amazing I won. I was running against peace, prosperity, and incumbency."
            George W. Bush speaking to Swedish Prime Minister unaware a live television camera was still rolling, June 14, 2001
            Selfcode: ie:% fl:( br:> va:) ls:& fo:) rl:? n4:& ss:| de:] js:| ch:? sh:( mo:) zu:)
            1. Hallo,

              Was ich wissen möchte ist, ob der Compiler bei p++; nicht um 1 Byte oder Bit erhöht, sondern um die Größe der Struktur auf die p zeigt.
              Ich hoffe, ich konnte mich verständlich ausdrücken.

              Absolut verständlich, ja. :-)
              Und ja, es ist wirklich so, dass der Zeiger bei Additionen, Subtraktionen, oder auch Increments oder Decrements immer in Schritten erhöht oder verringert wird, die der größe des zugeordneten Datentyps entsprechen. Bei BYTE* also um ein Byte, bei DWORD* um vier Byte, bei structs um die jeweilige Strukturgröße.
              Nur bei void* versagt die Arithmetik, gerade weil das ein typenloser Zeiger ist und die Größeninformation fehlt. Deswegen sind solche Operationen für void* auch nicht definiert (was ich gelegentlich bedaure).

              Schönes Wochenende,

              Martin

              --
              Lieber Blödeleien als blöde Laien.
    2. Hi

      Wichtig ist, dass ich später von dem am Anfang von malloc() erhaltenen Pointer wieder zum Integer-Flag oder zum zweiten Block finde (Die grössen sind bekannt).

      Dann könntest du auch entsprechenden Speicherplatz allokieren und dir den Zeiger auf eine solche Struktur zurückgeben lassen.

      Genau diesen ersten Zeiger erhalte ich(In der theoretischen Aufgabe Vorgegeben) z.B. für 64 MB allozierten Speicher. Danach muss ich diesen mit Hilfe einer linked List verwalten. Der 1. Block soll zeigen ob der Block leer oder besetzt ist und der 2. Block soll den Pointer zum nächst freien Block enthalten...der 3. Block enthält dann die Daten.

      Wie kann ich nun in einen 2. Block einen Pointer platzieren?

      Es ist in C durchaus möglich Zeiger zu inkrementieren bzw. bitweise zu erhöhen. [1]

      Wie? ;-)

      Wie erhalte ich den Wert des flags (0 oder 1) wieder wenn ich den Pointer zu area gegeben habe?

      Du machst es dir einfach zu schwer. Wie um alles in der Welt willst du darauf zugreifen? Du müsstest dir alles auseinanderfriemeln. Mach es dir nicht so schwer und arbeite mit einer ordentlichen und sauberen Struktur.

      Wie gesagt, eine theoretische Aufgabe in welcher es darum geht das Speichermanagement zu "simulieren".

      Gruss Marcel

  2. Sup!

    Warum willst Du eigentlich den Speicher selbst verwalten und irgendwelche Blöcke anlegen? Glaubst Du, Du kannst das besser als die Speicherverwaltung vom OS?

    Gruesse,

    Bio

    --
    Never give up, never surrender!!!
    1. Warum willst Du eigentlich den Speicher selbst verwalten und irgendwelche Blöcke anlegen? Glaubst Du, Du kannst das besser als die Speicherverwaltung vom OS?

      Oh Bio, he never gives up, he'll never surrender :-)))

      Freundliche Grüße

      Vinzenz

      1. Hallo

        Wie gesagt, es handelt sich um eine theoretische Aufgabe...

        Gruss Marcel

    2. Hallo

      Warum willst Du eigentlich den Speicher selbst verwalten und irgendwelche Blöcke anlegen? Glaubst Du, Du kannst das besser als die Speicherverwaltung vom OS?

      Nein, aber es handelt sich um eine theoretische Aufgabe, in welcher die Allokations-Prinzipen (z.B. Next-Fit oder Best-Fit) in einer dynamischen Speicherverwaltung mit Hilfe einer Linked-List (doubly oder single) implementiert werden sollen...

      Gruss Marcel

  3. Hallo,

    Hallo

    Ich alloziere mit Hilfe von malloc() Speicher. Malloc() gibt mir den Pointer zum allozierten Speicher zurück.
    Nun möchte ich den reservierten Speicher in Blocks aufteilen, sodass ich den ersten Block z.B. für ein Integer-Flag, den zweiten Block für eine Pointer-Adresse und den dritten Block für andere Daten nutzen oder noch weiter aufteilen kann.

    Wenn Du Dir die Startadresse und die Offsets der jeweiligen Blöcke merkst, dann sollte die ganze Übung kein wirkliches Problem sein. Alternativ kannst Du natürlich die Offset bei jedem Zugriff selbst ermitteln, was zwar Speicherplatz spart, sich aber negativ auf die Geschwindigkeit auswirken wird. Und dann gäbe es ja immer noch Variante bei der Du Dir für jeden Block die Startadresse merkst.

    In Deinem Beispiel wird der Variable area zweimal ein Wert zugewiesen. Das solltest Du unterlassen, da Du ja, wenn Du area überschreibst, nicht mehr die Startadresse rekonstruieren kannst.

    Grüße
      Klaus