Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Meandor

Frischling

  • »Meandor« ist der Autor dieses Themas

Beiträge: 55

Wohnort: Oberhausen

Beruf: Student

  • Private Nachricht senden

1

10.01.2006, 01:24

Heap vs. Stack

Liebe Spieleprogrammierer!

Ich würde gerne mal wissen welcher der beiden Speicherbereiche "Heap" oder "Stack" besser sind von der Performance. Gibt es dort überhaupt Unterschiede und wenn welche?

Das kommt aus dem Buch nicht deutlich hervor.

Ist es jetzt besser ein Array[100] auf dem Stack zu speichern oder auf dem Heap.

Da man ja auch ohne new und delete seine Instanzen von Klassen initalisieren kann.

C-/C++-Quelltext

1
2
3
4
1:      CRaumschiff Spieler1; //Instanz Spieler1 befindet sich auf dem Stack


2:      CRaumschiff *pRaumschiff=NULL;
         pRaumschiff = new CRaumschiff; // Der Zeiger von pRaumschiff zeigt auf eine Instanz die sich auf dem Heap befindet.


Allgemeine Feststellung zu 1: Instanz wird kurz und schmerzlos erzeugt+. :top:
(Klar muss dabei festliegen das Spieler1 aufjedenFall erzeugt wird. -Man ist nicht mehr dynamisch mit der Verwaltung während der Laufzeit.)- :down:

Zu 2: Irgendwie alles sehr viel schwieriger zu verstehen und zubegreifen.Man benötigt viel mehr Tipperei vorallem muss man sich auch noch darum kümmern das man mittels delete den Speicher wird frei gibt und anschließend noch auf NULL setzen. :down:
2. Man hat jetzt nicht mehr wirklich schöne Namen für seine Instanzen wie pRaumschiff (nicht sehr aussagekräftig :down: da es sich um einen Zeiger handelt der irgendwo auf dem Heap zeigt.)

Meine Frage ist jetzt was soll das ganze? Wenn man schon im Vorfeld weiß das ich z.B. 10 Instanzen einer Klasse benötige dann bitte doch auf dem Stack. Oder ist der Heap schnell was den Zugriff angeht. Und es lohnt sich doch der Aufwand? Auf dem Heap nur Instanzen erzeugen bei denen ich während der Laufzeit nicht sicher bin und dynamisch wärend des Spiels erzeugt werden müssen. Man darf ja auch gleichzeitig Instanzen auf dem Stack und dem Heap haben.

Insofern ist doch zum Beispiel das HTML Logfile welches Heiko in dem Buch erzeut ein Fall für den Stack oder? (Singletons hin oder her)
Es ist doch im Vorfeld klar das eine Logfile Instanz erzeugt werden muss-- dann doch direkt auf dem Stack oder??

Beispiel:
So weiß ich doch z.B. das

C-/C++-Quelltext

1
CRaumschiff Spieler1; 

wirklich der Spieler1 ist mit seinen Werten und

C-/C++-Quelltext

1
CRaumschiff Endboss; 

der hat eben die Endboss Werte.

Besitze ich dagegen nur Zeiger dann weiß ich doch gar nicht mehr bei pRaumschiff =new Craumschiff [10];
das pRaumschiff[5] z.B. der Endboss ist. Das ist doch super kompliziert.

Wäre schön wenn man einfach mal was dazu schreibt! Und die Sache hier mal wirklich durch diskutiert. Vielleicht habe ich ja auch nur die falsche Einstellung -- lass mich auch gerne überzeugen. Da ich die Dinge gerne richtig verstehen möchte!

Mit bestem Gruß
Jens

Anonymous

unregistriert

2

10.01.2006, 04:06

Nimm mal die Werte deines einfachsten Projekts und multipliziere alle Werte mit 1000.

Gib dann noch jedem Objekt mindestens 3 Attribute(Werte= NewBattleship(1000)=ship([energie]100,[firep,random]10,[status]1).

Dein "auf 0 setzten" sollte eigentlich durch eine von dir programmierten Funktion erledigt werden.[colli-->(kill.ship)]

Und nun denk mal in Ruhe nach welche Schleifen der Rechner wann verarbeiten muss.(Auch in der Hinsicht das du eventuell Sachen in ein weiteres Objekt mitnehmen willst)

cu

koschka

Community-Fossil

Beiträge: 2 862

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

3

10.01.2006, 09:27

ja ich glaub da gibts nicht viel zu diskutieren....

Man benutzt Zeiger ja nicht nur um Variablen dynamisch zu allocieren, sondern für vieles, andere auch.

Wenn du direkt auf dynamische Speicherbelegung aus bist:
- Man benutzt dynmaische Variablen, wenn man nicht wiess wie groß das Array im Laufe des Programmmes werden soll. Man kann so einfach Speicherbereich dranhäängen und verschwendet nicht massig, was bei Raumschiff[1000] ja wohl der Fall wäre. Man kann mit Raumschiff[1000] ja auch nicht mehr als max 1000 Schiffe "konstruieren" im Speicher. Was macht man wenn man mehr braucht --- Fehler ausgeben? Nein. Man benutzt dynammischen Speicher und benutzt am besten noch eine Liste.

- Man benutzt statische Variablen wenn man GENAU weiss wie groß man Speicher braucht. Z.B. bei einer Warrteschlange wo man nur x Elemente anfügen kann, wenn es mehr sind, wird das erste abgetrennt.

Das ist eigentlich das ganze Geheimnis. Im übrigen kannst du schreiben was du willst ... du musst nicht pSchiff schreiben!
Und das p schreibst du ja auch nur wegen dem Pointer. Allgemein gesehen ist es wesentllich besser Zeiger zu verwenden, weil Zeiger eine feste Größe haben, egal welcher Typ. Normalerweise 4Byte, bei 32 Bit Prozessoren (32/8). Es ist also egal ob du eine Zahl hast oder eine 512 Textur! ... was besonders beim sortieren ne Menge Kopieraufwand und damit Zeit spart!

Steven77

Alter Hase

Beiträge: 515

Wohnort: Münster - Gievenbeach

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

4

10.01.2006, 11:15

Die Zugriffszeiten sind übrigens dieselben, denn -- ob Stack oder Heap -- es liegt ja beides in ein und dem selben Hauptspeicher (mal abgesehen von irgendwelchen Page-Faults wegen Auslagerungen, aber das ist 'ne andere Geschichte und betrifft auch wiederum beides).

Meandor

Frischling

  • »Meandor« ist der Autor dieses Themas

Beiträge: 55

Wohnort: Oberhausen

Beruf: Student

  • Private Nachricht senden

5

10.01.2006, 11:41

Zitat


Dein "auf 0 setzten" sollte eigentlich durch eine von dir programmierten Funktion erledigt werden.[colli-->(kill.ship)]


Klar kann man sich noch die Schreibarbeit sparen mit diesem SAFE_DELETE Makro im Kapitel 8.9 das meine ich aber nicht.

Zitat


Nimm mal die Werte deines einfachsten Projekts und multipliziere alle Werte mit 1000.


Ist richtig wenn ich also zum Beispiel möchte das alle meine Schiff durch ein POWERUP ihre Lebensenergie um 1000 erhöhen sollen, dann sind die Instanzen auf dem Heap über eine Schleife wesentlich leichter zu verwalten.

C-/C++-Quelltext

1
2
3
for (int i=0; i<MaxAnzahl; i++)
{  pRaumschiff[i].Lebensenergieum1000_erhoehen();//Memberfunktion

}


mittels Instanzen wäre das mehr schreibaufwand dabei gebe ich dir recht!
Aber warum sollte jetzt plötzlich mein "Endboss" der an Stelle 4 steht auch direkt erhöht werden.
Ich hoffe man versteht was ich meine also ich muss doch jedes mal wissen welches konkrete Objekt sich hinter diesem Zeiger verbirgt.
pRaumschiff[0] //normales Raumschiff.
pRaumschiff[1] //das Heldenraumschiff
pRaumschiff[2] //normaler Gegner
pRaumschiff[3] //Endboss

das soll jetzt nur ein Beispiel dafür gewesen sein. Hätte ich aber CRaumschiff Endboss; erzeugt dann könnte ich mit Endboss.Lebensenergie_reaktivieren(); gezielt diese eine Instanz ansprechen. (Da diese mit einem vernüftigen Namen belegt worden ist!)

Zitat


Was macht man wenn man mehr braucht --- Fehler ausgeben?

Klar wenn ich zur Laufzeit nicht weiß wieviele Raumschiffe ich benötige komme ich an dynamische Arrays auf dem Heap nicht vorbei. Am besten noch alles in eine Liste von der STL packen. Wie das aber genau geht muss ich nochmal schauen damit beschäftige ich mich ja zur Zeit.

Auch gebe ich Koschka recht ein Zeiger spart Zeit, da nur 4 Byte im Gegensatz zu großen Instanzen oder Strukturen benötigt werden.

Wie groß ist den der Heapspeicher und der Stackspeicher denn genau?
Also beide befinden sich ja irgendwo im RAM-Speicher. Also wenn mein PC 512MB DDR Speicher besitzt dann sind wieviel davon Stack und wieviel davon Heap oder überschneiden die sich sogar und können sich gegenseitig blockieren falls beide Bereiche zugroß werden?

Gibt es denn jetzt Geschwindigkeitsunterschiede zum Heap und Stack?[/cpp]

Helmut

5x Contest-Sieger

Beiträge: 692

Wohnort: Bielefeld

  • Private Nachricht senden

6

10.01.2006, 16:02

Hi!
Der Stack ist unter Windows standardmäßig genau 1 MB groß. Das reicht aber normalerweise völlig aus.
Der Heap ist theoretisch nur nach verfügbarem RAM+Auslagerungsdatei begrenzt.

Also grundsätzlich sollte man, wann immer möglich, den Stack benutzen, da das Allocieren etwas schneller ist und die Variable einfacher zu benutzen ist.
Wenn man aber größere Daten braucht, Polymorphie benutzt, die Größe der Daten nicht konstant ist oder die Daten nach Funktionsende weiterverwendet werden müssen kommt man um den Heap nicht herum.

Ciao

Lemming

Alter Hase

Beiträge: 550

Beruf: Schüler

  • Private Nachricht senden

7

10.01.2006, 17:13

jopp. und wenn du sehen willst, was passiert, wenn dein ram mal voll ist. probiers einfach mal aus. ist leider nicht sehr aufregend :( nur extrem langsam *g*.

zum thema

Zitat


pRaumschiff[0] //normales Raumschiff.
pRaumschiff[1] //das Heldenraumschiff
pRaumschiff[2] //normaler Gegner
pRaumschiff[3] //Endboss

es ist auch möglich daten in assoziativen listen zu speichern zB std::map
dann kannst du deinen endboss mit dem namen "Endboss" und deinen Helden mit dem Namen "Held" speichern. Oder du fügst in die CRaumschiff einfach noch ne variable ein, die bestimmt was für einer das ist.
Es gibt Probleme, die kann man nicht lösen.
Für alles andere gibt es C++...

Meandor

Frischling

  • »Meandor« ist der Autor dieses Themas

Beiträge: 55

Wohnort: Oberhausen

Beruf: Student

  • Private Nachricht senden

8

10.01.2006, 18:28

Also was Helmut schreibt ist ja völlig anders als was Steve77 schreibt. Was ist denn nun richtig?
Dauert es länger oder nicht?
(Würde mich tendenziell auch auf die Meinung von Helmut setzen)

Also nur bei dynamischen Datenstrukturen den Heap verwenden.

Was sollte man denn machen wenn ein Array[10000] größer als 1 MB wird und man weiß das schon im Vorfeld? Hat man dann nur noch die Möglichkeit mit einem "lahmen" Programm zu arbeiten?

Mal eine ganz andere Frage zum Heap:
Ich versuche eine dynamische Struktur auf dem Heap zu erzeugen!
Dabei versuche ich diese in eine Liste der STL zu schreiben. Mittels dem Listing 9.5 vom Heiko
Jetzt habe ich schon sehr lange daran rumprobiert und kriege es einfach nicht hin.

Ich schicke einfach mal den Code der schon mal funktioniert!

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <list>
#define GEMELDET 'G'

using namespace std;//ich weiß das man namespace weglassen sollte


struct Auftragsliste //Hier ist die Struktur die dynamisch werden soll

{
    double Zeit;
    int Wert;
    char Status;
};

int main()
{
    int Anzahl =0;
    list<Auftragsliste*> lAuftrag; //Liste vom Typ der Struktur Auftragsliste

    list<Auftragsliste*>::iterator i;
    Auftragsliste *Temp = NULL;
    cout <<"Wie viele Aufträge erzeugen?"<<endl;
    cin>> Anzahl;
//Hier werden entsprechend Instanzen auf dem Heap erzeugt und in die Liste geschrieben

    for (int j=0; j<Anzahl; j++) 
    {   Temp = new Auftragsliste;
        lAuftrag.push_back (Temp);
    }
    int nummer=0;
    for (i=lAuftrag.begin(); i!=lAuftrag.end(); ++i)
    {
        (*i)->Wert = nummer;
        (*i)->Zeit = 500;
        (*i)->Status = GEMELDET;
        nummer ++; //soll eine fortlaufende Nummer werden

    }
    int index=0;
    for (i=lAuftrag.begin(); i!=lAuftrag.end(); ++i)
    {   
        cout<<"DAS ELEMENT "<<(*i)->Wert<<" wird bei der Zeit :" <<(*i)->Zeit<<" der Status : ";
        if((*i)->Status==GEMELDET)
        cout<<"GEMELDET"<<endl;
        index++;
    }
lAuftrag.sort();//Dieser Befehl funktiont doch auch


    for (i=lAuftrag.begin(); i!=lAuftrag.end(); ++i)
    {cout<<"DAS ELEMENT "<<(*i)->Wert<<" wird bei der Zeit :" <<(*i)->Zeit<<" der Status : ";
        if((*i)->Status==GEMELDET)
        cout<<"GEMELDET"<<endl;
    }

    for (i=lAuftrag.begin(); i!=lAuftrag.end(); i++)//Heap wieder frei machen

    {
        delete (*i);
        (*i) = NULL;
    }
    lAuftrag.clear(); //Liste löschen

    return 0;
}


Meine Frage ist jetzt wie bekomme ich einen Neuen Auftrag in diese Liste und zwar an einer bestimmten Position! Diese sollen jetzt einen Wert von z.B. 700 besitzen und die Nummer sollte fortlaufend sein.



C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for (j=0; j<3; j++)//Hier sollen noch einmal 3 Instanzen an die Liste hintendran gefügt werden

    {
        Temp= new Auftragsliste;
        lAuftrag.push_back(Temp);
    }

    
    for (i; i!=lAuftrag.end(); i++) //Wie erklärt man an dieser Stelle dem Compiler welches die letzte Instanz war?? Ab wann soll er zählen in der Schleife 

    {
            (*i)->Wert = nummer;
            (*i)->Zeit = 600;
            (*i)->Status = GEMELDET;
            nummer ++;
        
    }


Zitat


In Heiko Listing schreibt er in Zeile 45:
lDaten.insert(i, 7);


wie mache ich das jetzt bei meiner Struktur.
ich möchte an der 3. Stelle eine Instanz einfügen mit den Werten
(3, 900, GEMELDET)

mit

C-/C++-Quelltext

1
2
3
i = lAuftrag.begin();
i++;i++;i++; 
lAuftrag.insert(i, 3,900, GEMELDET);

gibt die Fehlermeldung Funktion ist überladen wie müsste ich denn so eine Funktion schreiben oder gibt es vielleicht andere Befehle von STL aus als insert??

Hätte gerne eine Antwort darauf oder einfach ein gutes Tutorial dazu!

[/quote]

Helmut

5x Contest-Sieger

Beiträge: 692

Wohnort: Bielefeld

  • Private Nachricht senden

9

10.01.2006, 22:33

Ne, im Grunde hat Steven77 nichts anderes als ich geschrieben.
Der new Befehl selbst dauert nämlich nur ein bisschen. Später darauf zuzugreifen sollte genauso schnell gehen, wie auf den Stack zuzugreifen.

Bei new muss Windows nämlich den ganzen RAM nach einer Lücke durchforsten, die groß genug für deinen angeforderten Speicher ist. Im worst case muss Windows sogar andere Daten rumverschieben oder sogar auf die Festplatte auslagern. Allerdings ist das ganze sehr gut optimiert, sodass es eigetnlich nicht merklich verlangsamt. Bei Java werden z.B. alle Instanzen auf dem Heap gespeichert (gut, Java ist auch langsam:) )
Beim Stack jedenfalls wird intern einfach nur ein Zeiger erhöht, wenn man in eine Funktion springt, schneller geht's also nicht.

Zu deinem Problem:
ich würds einfach so machen:
lAuftrag.insert(i, new Auftragsliste(3,900, GEMELDET));

Wobei du dann noch den entsprechenden Konstruktor machen müsstest.
(Und Auftragsliste würde ich eher Auftragselement oder einfach Auftrag nennen;) )

Ciao

Meandor

Frischling

  • »Meandor« ist der Autor dieses Themas

Beiträge: 55

Wohnort: Oberhausen

Beruf: Student

  • Private Nachricht senden

10

10.01.2006, 23:44

Helmut du bist ein Schatz könnte dich echt Küssen! :-)
Das das so einfach ist hätte ich jetzt auch nicht gedacht!
Also einfach aus der Struktur eine Klasse machen mit einem Konstruktor und dann klappt es auch schon.
Helmut du hast was bei mir gut!

Wunderbar!!

Jetzt habe ich endlich meine dynamische Liste! Funktioniert für das was ich brauche aber ich habe einfach mal wieder rumprobiert und hänge wieder.
Habe ein paar Fragen zu den Listen Funktionen z.B. sort(), reverse(), remove(),

Bei der Liste vom Heiko klappt das ja wunderbar sind ja leider auch nur int Werte gespeichert.

wenn der lDaten.sort(); schreibt dann steht die Liste 1A sortiert.

1,2,2,2,3,3,4,5,6,7, usw.

wenn ich das bei einer Struktur bzw. Klasse mache in der Liste dann sortiert er diese aber ich sehe dabei keine Logik. Wenn ich die Struktur mit seinen 3 Elementen ausgebe.

In meinem Fall er sortiert weder nach dem Wert noch nach der Zeit oder dem Status. Aber die Liste wird auf jeden Fall umsortiert.

Kann ich der Funktion sort() den Befehl mit geben nach welchem Element er die Liste sortieren soll?

Wie kann ich mit der Funktion remove(); dem Compiler klar machen das er die Elemente mit dem Status GEMELDET löschen soll.
Geht das?
Heiko schreibt remove (7) und schon sind alle Elemente mit 7 aus der Liste verschwunden!

Ich mein klar man kann das umgehen in dem man einfach eine Prüffunktion schreibt die die Liste durchläuft und bei
(*i)->Status==GEMELDET die Position ausgibt und dieses Element wird dann mittels lAuftrag.erase(Position) gelöscht.

Aber mittels remove() müsste das doch in einem Ruck gehen.
Frage wäre also was muss man der remove Funktion übergeben das diese direkt alle Elemente löscht.

Werbeanzeige