Heap Grundlagen: new, delete und Zeigerarithmetik



  • Ich habe folgende 3 Fragen:

    Ich erstelle ein Zeiger auf ein Array im Heap.

    char *pHeap = new char[500];

    und lösche den Speicherplatz wieder mit

    delete [] pHeap;

    Nehmen wir jetzt an, in dem reservierten Array auf dem Heap befindet sich wiederum ein Zeiger auf einen neuen Bereich im Heap.

    Wenn ich nun

    delete [] pHeap;

    ausführe, wird dann der Speicherplatz auf den der Zeiger im Array zeigt, mitgelöscht? Oder ist der weiter reserviert?

    char *pHeap = new char[500];
    char *pZeiger = pHeap;

    pHeap = "hallo"; // führt zu einem Fehler
    pZeiger = "hallo"; // geht

    Wieso das? Es gilt ja *pHeap == *pZeiger

    Kann ich mich darauf verlassen, das bei Beendung des Programmes aller Heap-Speicher wieder freigegeben wird? Oder erhalte ich diesen Speicher erst bei Reset des PC's zurück? So wäre es ja in bestimmten Ausnahmesituationen vertrebar, den Speicher nicht im Programm freizugeben.

    /edit pumuckl: Titel für FAQ angepasst.



  • 1. Dieser Speicherbereich wird nicht freigegeben. Das mußt Du schon selbst machen.

    2. pHeap="hallo" führt zu keinem Fehler! Du verlierst bloß den Speicher auf den pHeap zeigt.

    3. Unter Betriebssystemen von Windows und co. kann man sich wohl darauf verlassen. Es gibt jedoch Ausnahmen, z.B. wenn Du globale Ressourcen von Windows reservierst, die bleiben dann wohl blockiert. Außerdem gibt es sicher Systeme wo dies nicht der Fall ist (wenn nicht jeder Prozeß seinen eigenen Adreßbereich hat oder so). Weiterhin ist das wohl einfach schlechter Stil.

    ------------------
    Sincerely,
    void*

    Everything is BIGGER in TEXAS



  • zu 2.)

    char *var = "Hallo"

    ist doch ein Fehler und der Compiler sollte eigentlich diesen Code nicht zulassen. (Ich weiß, die meisten sind nicht so konsequent.)
    Man könnte damit so etwas schreiben:

    Code:


    char *var = "Hallo";

    var[1] = 'e';

    // ... oder auch das hier:

    strcpy(var, "Welt");

    [/code]


    Damit würde aber schreibend auf Speicher zugegriffen, der eigentlich read only ist. Zumindest Windows(NT)-Programme würden mit solchem Code aber gnadenlos abstürzen!

    Richtig wäre

    const char *var = "Hallo";

    und nur das sollte ein Compiler auch erlauben.

    zu 3.)

    Es ist im allgemeinen schlechter Stil, Speicher nicht wieder freizugeben, insbesondere dann, wenn die Möglichkeit besteht, daß ein "leichtsinnig" geschriebener Programmteil wiederverwendet wird und in der neuen Umgebung dann Probleme durch Speicherlöcher verursacht.

    Andererseits kann es aber auch Anwendungen geben, in denen es Sinn macht, auf das Freigeben zu verzichten, z.B. bei kleinen Tools die nur eine Datei durchsuchen (oder so...) und sich dann wieder beenden.

    Wenn man den Speicher nur mit C++-Mitteln anfordert, sollte das auf aktuellen Betriebssystemen keine Probleme verursachen, d.h. das System sollte den Speicher wieder verfügbar machen, sobald das Programm beendet wird. Das c++-new() wird mit Sicherheit keinen Spezialspeicher wie z.B. shared memory belegen, bei dem man wohl besser auf korrekte Freigabe achten sollte.

    Stefan.



  • Danke.

    Wieso Read-Only?

    Meinst du, weil das Array so ev. zu klein deklariert ist?

    Noch etwas misteröses:

    char *pHeap = new char[500];
    char *pZeiger = pHeap;

    pHeap++; //führt zu Absturz
    pZeiger++; //geht

    confused...



  • read only:
    Der Compiler wird Stringliterale wie "Hallo" idR in einem speziellen Segment des Programms ablegen, auf das nur Lesezugriff erlaubt ist (zumindest unter Windows, mit anderen Systemen kenne ich mich nicht aus). Das ist ja auch naheliegend, da Stringliterale per Definition konstant sind. Wenn man nun schreibend auf diese Daten zugreift, kriegt das System nen dicken Hals und schießt das Programm ab.

    Diese Zeile

    pHeap++;

    führt an sich bestimmt nicht zum Absturz. Ich vermute, es wird wohl eher daran liegen, daß Du danach noch ein

    delete[] pHeap;

    machst. Hier versucht das Laufzeitsystem dann, den Speicher, auf den pHeap zeigt wieder freizugeben und macht Ärger, da der ursprüngliche Wert des Pointers verändert wurde und nicht mit der Rückgabe von new() übereinstimmt. Versuche mal das:

    char *pHeap = new char[500];
    // ...
    pHeap++;
    // ...
    pHeap--;
    delete[] pHeap;

    Jetzt stimmt der Zeiger wieder und delete() sollte funktionieren.

    Normalerweise sollte man übrigens nicht Zeiger, die noch an delete() übergeben werden sollen auf irgend eine Weise ändern. Ist einfach sicherer....

    Stefan.



  • Alles begriffen.

    thx

    Und was ist wenn ich 2 Strings addieren möchte?

    z.B.
    const char *StringA = "hallo";
    const char *StringB = "Stefan";

    const char *StringA_und_StringB = StringA + StringB;

    [ 04.07.2001: Beitrag editiert von: class CMarcus ]



  • Das geht nicht. Hier addierst Du Pointer, nicht den Inhalt der Strings.

    Falls Du tatsächlich mit C-Strings arbeiten möchtest, mußt Du eine Funktion verwenden (und das Ziel kann natürlich nicht mehr const sein):

    Code:


    #include <cstring>

    #include <iostream>

    // oder (altes C++):

    // #include <string.h>

    // #include <iostream.h>

    int main() {

    const char *s1 = "Hallo ";

    const char *s2 = "Waldgnurpf!";

    char s3[100]; // nix const!!!

    strcpy(s3, s1); // Kopiere s1 nach s3.

    strcat(s3, s2); // Hänge s2 hinter den Inhalt von s3.

    std::cout << s3 << std::endl;

    return 0;

    }

    [/code]


    Viel leichter geht das ganze aber mit der Klasse string aus der Standardbibliothek:

    Code:


    #include <string>

    #include <iostream>

    int main() {

    const std::string s1 = "Hallo ";

    const std::string s2 = "Waldgnurpf!";

    const std::string s3 = s1 + s2; // const OK!

    std::cout << s3 << std::endl;

    return 0;

    }

    [/code]


    Tja, und dann geht auch das const wieder. Diese Klassen machen das Leben *echt* einfacher.

    Stefan.



  • Zitat:


    Original erstellt von Waldgnurpf:
    **Ich habe folgende 3 Fragen:

    Ich erstelle ein Zeiger auf ein Array im Heap.

    char *pHeap = new char[500];

    und lösche den Speicherplatz wieder mit

    delete [] pHeap;

    Nehmen wir jetzt an, in dem reservierten Array auf dem Heap befindet sich wiederum ein Zeiger auf einen neuen Bereich im Heap.

    Wenn ich nun

    delete [] pHeap;

    ausführe, wird dann der Speicherplatz auf den der Zeiger im Array zeigt, mitgelöscht? Oder ist der weiter reserviert?

    char *pHeap = new char[500];
    char *pZeiger = pHeap;

    pHeap = "hallo"; // führt zu einem Fehler
    pZeiger = "hallo"; // geht

    Wieso das? Es gilt ja *pHeap == *pZeiger

    Kann ich mich darauf verlassen, das bei Beendung des Programmes aller Heap-Speicher wieder freigegeben wird? Oder erhalte ich diesen Speicher erst bei Reset des PC's zurück? So wäre es ja in bestimmten Ausnahmesituationen vertrebar, den Speicher nicht im Programm freizugeben.

    [Diese Nachricht wurde von Waldgnurpf am 31-05-2001 editiert.]**


    char *pHeap = new char[500];
    char *pZeiger = pHeap; // unzulässig

    pHeap = "hallo"; // führt zu einem Fehler
    pZeiger = "hallo"; // absolut unzulässig!!

    Wenn du 2 zeiger auf die selbe variable willst dann musst du das so machen:

    char *pHeap = new char[500];
    char *pZeiger;
    pZeiger = pHeap;
    denn mit
    *pZeiger = pHeap; würde pHeap in die Zieladresse von *pZeiger gescrieben, und das passiert auch beim initialisieren

    man kann char *mwah = "hiereinpaarzeichen";
    nur beim initialisieren. danach muss man strcpy benutzten

    ---

    trotzdem benutzte ich fast nirgends mehr char-zeiger. die std::string klasse is viel besser



  • Bin jetzt ein wenig verunsichert. Könnt ihr kurz folgendes Beispiel kommentieren, und die Fehler oder Unsauberheiten erläutern?

    #include <iostream>
    using namespace std;
    
    class String
    
    {
    private:
       char *Text;
    
    public:
       char* Getname()
       {
          return Text;
       }
    
       SetName(char * name)
       {
          Text = name;
       }
    };
    
    int main()
    {
       String *pHeap = new String[5];
       String *pZeiger = pHeap;
    
       pZeiger->SetName("1");
       cout << pZeiger->Getname()<< endl;
       pZeiger++;
    
       pZeiger->SetName("2");
       cout << pZeiger->Getname()<< endl;
       pZeiger++;
    
       pZeiger->SetName("3");
       cout << pZeiger->Getname()<< endl;
       pZeiger++;
    
       pZeiger->SetName("4");
       cout << pZeiger->Getname()<< endl;
       pZeiger++;
    
       pZeiger->SetName("5");
       cout << pZeiger->Getname()<< endl;
    
       delete [] pHeap;
       return 0;
    };
    

    [Diese Nachricht wurde von Waldgnurpf am 01-06-2001 editiert.]

    [ Dieser Beitrag wurde am 03.11.2002 um 19:49 Uhr von Dimah editiert. ]



  • Das SetName kann Probleme geben, wenn du einfach nur den Zeiger kopierst.
    z.B.

    Code:


    void f(String str) {

    char test[]="Das gibt Probleme";

    str.SetName(test);

    }

    void main(){

    String a;

    f(a);

    cout<<a.GetName();

    }

    [/code]


    test wird da nämlich, nach beenden der Funktion f wieder gelöscht.
    Der Zeiger in String zeigt aber immernoch drauf. Wenn du jetzt den String verändern willst, gibts nen Ausnahmefehler, weil du im freien Speicher rumpfuschst.
    Du solltest den String also am besten wirklich kopieren: Größe des übergebenen Strings ermitteln, dann Text löschen und nochal neu mit der richtigen Größe erstellen und die Zeichen kopieren.
    Sonst stimmt glaub ich alles.



  • Hallo!

    @Stefan Dreckmann:

    Es _ist_ laut Standard erlaubt char* eine Zeichenkettenliteral zuzuweisen.
    char* x = "Das hier ist legal!"; (Abwärtskompabilität zu C und alten C++ Versionen)
    Es ist vom Typ const char[].
    Dieses Literal über einen Zeiger zu verändern ist jedoch ein Fehler, das Resultat ist undefiniert.
    Nachzulesen z.B. in "The C++ Programming Language" von B. Stroustrup (5.2.2).

    ------------------
    Sincerely,
    void*

    Everything is BIGGER in TEXAS



  • @void*:
    Im Ernst? Das ist Standard???
    Und ich dachte immer, daß bloß die Compiler hiermit nachlässig sind.
    Danke für den Hinweis.

    Stefan.



  • Im ernst! Ich habe hier gerade begonnen den neueste Ausgabe des Stroustrup zu lesen und bin zufällig gestern Abend über genau diese Stelle gestolpert.

    MfG
    void*



  • Danke erst einmal, ich werde wohl er einmal den alten Stil beibehalten.
    Noch was zu Thema Zeiger, ist es eigentlich guter Stil oder richtig, wenn man bei Feldern keinen Zeiger benutzt ? Sonst spuckt der Compiler immer eine Fehlermeldung heraus.
    z.B.

    Code:


    const char feld1[30];

    char* name = "Meier";

    strcpy(feld1,name);

    [/code]


    geht das auch anders ?



  • Hallo,

    wie meinst Du das mit "anders"?. Dein "Feld" ist ja nichts als ein const char-Array, daß heißt constanter Zeiger auf constante Daten.

    Falls Du meintest, ob man auf einen solchen Array Pointersyntax oder Subscript(Array)syntax anwenden sollte, dann heißt die Antwort:
    Aus Gründen der Lesbarkeit sollte man jeweils die verwendet werden mit der deklariert wurde !.
    Hält sich aber fast niemand dran <IMG SRC="http://www.c-plusplus.net/ubb/ubb/wink.gif">.

    Gruß

    [ 04.07.2001: Beitrag editiert von: class CMarcus ]


Anmelden zum Antworten