none
Speicherverwaltung uaf dem Stack / Heap RRS feed

  • Frage

  • Hallo,

    mir ist bewusst, was der Unterschied zwischen dem Heap und dem Stack ist, das ist ja nicht so schwer, aber eine Sache habe ich leider nicht ganz verstanden.

    Wenn ich folgenden Code habe, wird es auf dem Heap erstellt:

    MyClassObj * obj = new MyClassObj(10);
    

    Bei diesem Code wird es auf dem Stack erstellt:

    MyClassObj obj(10);

    Aber wo wird der Speicher reserviert, wenn ich folgenden Code ausführe?

    char * mystr = (char*)"Hallo liebe Welt\0"; 

    Ich nehme an, dass der String im Stack erstellt wird, aber stimmt das jetzt wirklich?

    Vor allem würde es doch bedeuten, dass ich, um den String auch nutzen zu können, wenn ich wieder raus aus dem Stack-Bereich bin, diesen erst auf den Heap kopieren müsste?

    Also wäre folgendes richtig?

    char* locStr = (char*)"Hallo Welt\0"; //Stack?
    
    char* globalStr = new char[strlen(locStr)]; //Heap
    
    //Kopieren den Strings
    strcpy(globalStr, locStr);
    
    
    // locStr muss nicht freigegeben werden, da es nur auf dem Stack liegt?
    
    //... globalStr kann nun überall verwendet werden, muss aber wieder freigegeben werden!
    
    delete[] globalStr;
    


    © 2015 Thomas Roskop

    Germany // Deutschland


    Freitag, 20. Februar 2015 20:28

Antworten

  • Hallo Thomas,

    da es ein Literal ist, würde ich vermuten, dass es nicht auf den Stack liegt. Den Cast auf char * solltest du nicht brauchen. Du kannst den Inhalt des Literals mal verändern und dann die Methode ein weiteres mal aufrufen, sequentiell oder rekursiv und gucken, was nun in mystr steht.

    Gruß Heiko


    Freitag, 20. Februar 2015 22:00
  • Hallo Thomas,

    (String) Konstanten wie oben die "Hallo..." werden als Konstanten definiert, der Compiler fast gleiche üblicherweise zusammen und sie wandern in das Daten Segment[1].

    Siehe u. a.: C String literals: Where do they go?.

    String literals inside functions: automatic variables or allocated in heap?.

    Als Selbsterfahrung: Aktiviere und schau Dir das Assembler-Listing (.cod) an, wenn Du es genau wissen willst.

    Gruß Elmar

    [1] Portable Executable Wikipedia (Siehe Links dort)
    • Als Antwort markiert Thomas Roskop Samstag, 21. Februar 2015 10:18
    Samstag, 21. Februar 2015 10:16
  • Und das es mit char* und char[] einen unterschied machen würde ist klar :D

    ...was ich damit meinte ist, daß bei Zuweisung eines Literals zu einem Array immer eine Kopie des Literalinhalts erzeugt wird. Deshalb kann man den Inhalt von char[] manipulieren, während man bei char *, das auf ein Literal zeigt, einen Laufzeitfehler erhält.

    in den Projekteinstellungen unter C++/Codegenerierung läßt sich das Stringpooling aktivieren, was bedeutet, daß Literale mit gleichem Inhalt nur einmal in der Assembly landen, wie Elmar schon angedeutet hat.

    void test()
    {
    	char	eins[] = "xxx",		// eins ist immer eine Kopie des Literals, unabhängig ob mit /GF oder /GF-.
    			zwei[4] = "yyy",
    			* drei = "zzz",
    			* vier = "xxx",
    			* fuenf = "xxx";	// Mit /GF gleiche Adresse wie vier, mit /GF- andere Adresse als vier.
    
    	*eins = 'A';
    	puts(eins);
    	*zwei = 'B';
    	puts(zwei);
    	*drei = 'C';	// Runtime-Fehler
    	puts(drei);
    }

    Gruß
    Heiko

    • Als Antwort markiert Thomas Roskop Samstag, 21. Februar 2015 13:30
    Samstag, 21. Februar 2015 13:24

Alle Antworten

  • Hallo Thomas,

    da es ein Literal ist, würde ich vermuten, dass es nicht auf den Stack liegt. Den Cast auf char * solltest du nicht brauchen. Du kannst den Inhalt des Literals mal verändern und dann die Methode ein weiteres mal aufrufen, sequentiell oder rekursiv und gucken, was nun in mystr steht.

    Gruß Heiko


    Freitag, 20. Februar 2015 22:00
  • Es dürfte übrigens auch einen Unterschied machen, ob man

    char * pStr = "Hallo";

    oder

    char str[] = "Hallo";

    schreibt, wenn ich mich richtig erinnere.

    Gruß Heiko

    Freitag, 20. Februar 2015 22:11
  • Danke für die Antworten,

    das bedeutet also, deiner Meinung nach Heiko,

    dass es auf dem Heap ist und somit auch global zugreifbar ist [auch von Caller der Funktion, die vor dem Stack steht]?

    Und das es mit char* und char[] einen unterschied machen würde ist klar :D

    PS: Eigentlich wäre das (char*)"text" unnötig, aber mein Compiler will es gerne in std::string verwandeln, weil er auch c++ serh gut kann, und sonst warnungne gibt. Und mit dem Cast hält er die Klappe.

    Ist aber auch nicht schlimm, da es keinen Overhead hat (ist ja beim Compilieren schon erledigt) und der übersichtshalber habe ich auch ein kleines Mako dafür erstellt:

    #define _r_t(s) ((char*)s)
    
    char* mstr = _r_t("Hallo Welt");
    //Wird zu folgendem aufgelöst:
    char* mstr = (char*)"Hallo Welt";
    


    © 2015 Thomas Roskop

    Germany // Deutschland

    Samstag, 21. Februar 2015 07:27
  • Hallo Thomas,

    (String) Konstanten wie oben die "Hallo..." werden als Konstanten definiert, der Compiler fast gleiche üblicherweise zusammen und sie wandern in das Daten Segment[1].

    Siehe u. a.: C String literals: Where do they go?.

    String literals inside functions: automatic variables or allocated in heap?.

    Als Selbsterfahrung: Aktiviere und schau Dir das Assembler-Listing (.cod) an, wenn Du es genau wissen willst.

    Gruß Elmar

    [1] Portable Executable Wikipedia (Siehe Links dort)
    • Als Antwort markiert Thomas Roskop Samstag, 21. Februar 2015 10:18
    Samstag, 21. Februar 2015 10:16
  • Hallo,

    danke Elmar, das erklärt dann wohl auch, warum Disassembler alle Strings direkt anzeigen können, z.B. bei IDA Pro im Strings-Fenster. Vielen Dank. 


    © 2015 Thomas Roskop

    Germany // Deutschland

    Samstag, 21. Februar 2015 10:18
  • Und das es mit char* und char[] einen unterschied machen würde ist klar :D

    ...was ich damit meinte ist, daß bei Zuweisung eines Literals zu einem Array immer eine Kopie des Literalinhalts erzeugt wird. Deshalb kann man den Inhalt von char[] manipulieren, während man bei char *, das auf ein Literal zeigt, einen Laufzeitfehler erhält.

    in den Projekteinstellungen unter C++/Codegenerierung läßt sich das Stringpooling aktivieren, was bedeutet, daß Literale mit gleichem Inhalt nur einmal in der Assembly landen, wie Elmar schon angedeutet hat.

    void test()
    {
    	char	eins[] = "xxx",		// eins ist immer eine Kopie des Literals, unabhängig ob mit /GF oder /GF-.
    			zwei[4] = "yyy",
    			* drei = "zzz",
    			* vier = "xxx",
    			* fuenf = "xxx";	// Mit /GF gleiche Adresse wie vier, mit /GF- andere Adresse als vier.
    
    	*eins = 'A';
    	puts(eins);
    	*zwei = 'B';
    	puts(zwei);
    	*drei = 'C';	// Runtime-Fehler
    	puts(drei);
    }

    Gruß
    Heiko

    • Als Antwort markiert Thomas Roskop Samstag, 21. Februar 2015 13:30
    Samstag, 21. Februar 2015 13:24
  • Das war mir schon bekannt, aber trotzdem, danke!

    Zum bearbeiten von diesen Konstanten Strings (char*) muss man diesen kopieren. Ähnlich geht auch der String in .NET vor, wenn er manipuliert wird, wenn ich mich nicht irre.


    © 2015 Thomas Roskop

    Germany // Deutschland

    Samstag, 21. Februar 2015 13:27