Speicherverwaltung bei C/C++

  • C

Es gibt 34 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Speicherverwaltung bei C/C++

    Hi,

    also ich dachte ich versuch ma auf C/C++ umzusteigen bzw meinen Horizont zu erweitern. Dabei bin ich auf dem Wikibook zu C auf das
    Thema Speicherverwaltung und das Thema Zeiger gekommen. Zeiger und GC alles schön und gut. Selbst die Verwendung von Zeiger hab ich
    mittlerweile auch drauf (denk ich jedenfalls xD). Was ich aber nicht verstehe ist folgendes: Ich hab das nachfolgende Programm

    C-Quellcode

    1. FILE *datei
    2. datei = fopen("testdatei.txt", "w+");
    3. int x = 5000;
    4. int *i = &x;
    5. fprintf(datei, "Speicheradresse %p enthaelt %i\n",(void *)i, *i);
    6. i++;
    7. fprintf(datei, "Speicheradresse %p enthaelt %i\n",(void *)i, *i);
    8. return 0;


    Und meines Verständnisses nach, weist der Compiler der Vaiable x irgendeine Adresse zu. Dann wird in diese Adrese als Inhalt
    der Wert 5 geschrieben. Dann wird im Programm der Pointer i bzw die Adresse auf die i zeigt um 1 (bzw 4 Byte):

    Speicheradresse 0046FC24 enthaelt 5
    Speicheradresse 0046FC28 enthaelt -858993460

    Ich hab das ein paar mal gemacht und jedes mal kommen zwar verschiedene Speicheradressen raus. Aber immer die selbe Kombination "...enthält
    5" und "...enthält -858993460".

    WARUM?

    Is des so weil der Compiler alle nicht verwendeten Speicherzellen mit dem Wert belegt? (War meine Theorie, die mir am plausiebelsten
    vorkommt.)
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Was passiert, wenn du statt:

    Radinator schrieb:

    C-Quellcode

    1. fprintf(datei, "Speicheradresse %p enthaelt %i\n",(void *)i, *i);
    2. i++;
    3. fprintf(datei, "Speicheradresse %p enthaelt %i\n",(void *)i, *i);


    lieber

    C-Quellcode

    1. fprintf(datei, "Speicheradresse %p enthaelt %i\n", &x, x);
    2. fprintf(datei, "Speicheradresse %p enthaelt %i\n", ++&x, x);


    schreibst?

    Und was bedeutet eigentlich %p und %i (schon zu lange her)?
    %p is der formatierte Pointer und %i ist der formatierte Integer(-wert) (hier)
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    also das fprintf(datei, "Speicherzelle %p enthaelt %i\n", %x,x);... bringt bei mir eine Fehlermeldung (muss dazu sagen, dass ich den Code mit VS-Express 2013 schreibe) "Der Wert muss ein änderbarer Ivalue sein!"

    Ich habs ma ohne das Pointer Typecasting versucht

    C-Quellcode

    1. fprintf(datei, "Speicheradresse %p enthaelt %i\n", i, *i);
    2. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);


    und hab einfach die Adresse des Pointers erhöht.
    Selbes ergebnis (sich ändernde Adressen aber gleiche werte)
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    ach verdammt...sry meinte '&' ich vertausch die zwei Zeichen wenn ich schnell schreibe und meistens bemerk ich das dann erst beim kompillieren^^
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Das Problem ist folgendes: Man kann nur den Wert der Pointer-Variable erhöhen, nicht aber die Adresse der Variable in der was gespeichert wurde (sprich du/ich kannst/kann i++ oder *i++ machen (also den Adresswert oder den Inhalt der Speicherzelle von Pointer Variable *i also von 4FFE auf 4FFF z.b.) ich kann nicht &x++ oder ++&x (Präinklrement - Postinkrement bzw Inkrement allgemein))
    Deswegen geht deine Lösung leider nicht.

    Bzw um was es mir ging ist folgendes. Warum kommen bei jedem neuen Programmaufruf andere Adressen(is klar), aber selbe Speicherzelleninhalte heraus?
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Ich habe von C/C++ kaum ne Ahnung, aber ist vielleicht -858993460 der Standardwert einer nicht beschriebenen Speicherzelle? Immerhin würde die Zahl Binär so aussehen 1100 1100 1100 1100 1100 1100 1100 1100. Teste es doch mit weiteren Speicherzellen und nicht nur mit zwei. Wenn weitere unbeschriebene Zellen das Selbe zurückgeben, dann denke ich ist klar, wieso die selben Werte Erscheinen.
    @sonne75: Ich hab in meinem ersten Code ausschnitt versehentlich noch die 5000 drinnen. Ich habs mit 5 und mit 5000 versucht und vergessen es in 5 umzuwandeln. Deswegen 5000 und nicht 5
    Bzw es spielt auch keine Rolle, welcher wert da is, mir gehts nur darum, warum ich, wenn ich die Adresse des Pointers um 1 inkrementiere bzw umd 4 Bytes, warum ich dann immer den selben wert bekomme.


    @EaranMaleasi: Also ich habs jetzt ein paar öfter mal versucht:

    C-Quellcode

    1. fprintf(datei, "Speicheradresse %p enthaelt %i\n", i, *i);
    2. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    3. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    4. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    5. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    6. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    7. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    8. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    9. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    10. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    11. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    12. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);
    13. fprintf(datei, "Speicheradresse %p enthaelt %i\n",++(i),*i);


    Quellcode

    1. Speicheradresse 0046FDBC enthaelt 5
    2. Speicheradresse 0046FDC0 enthaelt -858993460
    3. Speicheradresse 0046FDC4 enthaelt -858993460
    4. Speicheradresse 0046FDC8 enthaelt 258577608
    5. Speicheradresse 0046FDCC enthaelt -858993460
    6. Speicheradresse 0046FDD0 enthaelt 872392273
    7. Speicheradresse 0046FDD4 enthaelt 4652580
    8. Speicheradresse 0046FDD8 enthaelt 17897913
    9. Speicheradresse 0046FDDC enthaelt 1
    10. Speicheradresse 0046FDE0 enthaelt 5734056
    11. Speicheradresse 0046FDE4 enthaelt 5728848
    12. Speicheradresse 0046FDE8 enthaelt 872392097
    13. Speicheradresse 0046FDEC enthaelt 0
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Es war mir ja auch nicht wichtig zu erfahren was er zeigt. Ich wollte einfach nur wissen, ob jemand weis, WARUM zwar verschiedene Adressen, aber jedes mal die darauffolgende Zelle den selben Wert hat
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Der Blinde erklärt dem Einäugigen die Farbe blau? xD
    Da mach ich auch mit : )

    Also zunächst fehlt in deinem Code oben ein fclose ; )

    Der Compiler weißt der Variable x (AFAIK) nicht *irgendeine* Adresse zu. Die Adresse ergibt sich aus dem Wert des Stackpointers (zur Laufzeit) plus (oder minus) einem Konstanten Wert (vom Compiler) der von der Anzahl und Art der vorherigen Variablendefinition abhängt.

    Und der Compiler macht mit dem Speicher genau eine Sache: nichts. In C gibt es keinen Standardwert oder ähnliches.
    Man kann das leicht ausprobieren mit int foo; printf("%d", foo);. Der Compiler spuckt evtl. eine Warnung aus, ist aber gültiger Code. Der Inhalt einer nicht initialisierten Variable ist einfach was gerade im Speicher liegt, also mehr oder weniger Zufall.

    Warum 5 ausgegeben wird, sollte klar sein. Bei i++ erhöhst du die Adresse um 4 (bzw. 8 bei 64bit), wie du schon geschrieben hast. Jetzt ist die interessante Frage: worauf zeigt i?

    Mal angenommen du rufst deinen Code direkt aus main auf und hast sonst nichts weiter in der Funktion. Weiterhin gehen wir mal klassischerweise davon aus, dass der Stack von High nach Low wächst. Der Stack sieht dann in etwa so aus:

    Quellcode

    1. argc (high)
    2. argv
    3. datei
    4. x
    5. i (low)

    Dadruch das i inkrementiert wird, zeigt der Pointer nicht mehr auf x sondern auf datei. Beim dereferenzieren bekommst du nun die ersten 4 (bzw. 8) Bytes von datei. Normalerweise ist FILE nur ein typedef für ein struct. Und was da drin steht, hängt vom Aufbau des structs und letztlich vom Betriebssystem ab. Bei mir ist das z.B. gerade 0x77C2FCE0.
    Soweit das Standardmodell. Ob sich der Compiler von VS daran hält ist eine andere Sache. Dazu kommen dann evtl. noch Techniken zum Speicherschutz etc. die das Ergebnis beeinflussen können. Du kannst ja mal schauen ob i und datei auf die selbe Adresse zeigen: printf("%p %p\n", i, &datei);

    Ungeachtet dessen sollte klar sein, dass das ganze für den praktischen Einsatz nicht zu gebrauchen ist.
    Danke für deine erschöpfende Erklärung :)

    und zu deiner anregungn printf("%p %p\n", i, &datei); => die zwei untescheiden sich um 8 Bytes (0037F84C und 0037F854)

    aber trotzdem noch ma danke!
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Natürlich klappt das nich, weil x kein array ist.

    Was hast du den gedacht sollte da rauskommen?

    Guck ma das:


    C-Quellcode

    1. FILE *datei;
    2. datei = fopen("testdatei.txt", "w+");
    3. int x[] = {5000,2500};
    4. int *i = &x[0];
    5. fprintf(datei, "Speicheradresse %p enthaelt %i\n",(void *)i, *i);
    6. i++;
    7. fprintf(datei, "Speicheradresse %p enthaelt %i\n",(void *)i, *i);

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RushDen“ ()

    Radinator schrieb:

    Is des so weil der Compiler alle nicht verwendeten Speicherzellen mit dem Wert belegt?

    Nö. Das was du da machst endet in undefined Behaviour. Nach dem Inkrementieren zeigt i auf irgendwas (nur nicht x). Entweder zeigt das auf etwas anderes in deinem Program und du rufst dessen Wert ab, oder auf Speicher einer anderen Anwendung und es knallt. Beides kann passieren, soetwas tut man nicht.
    Oder wolltest du den Wert inkrementieren (*i)++ ?