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

TigerClaw25

unregistriert

1

05.07.2017, 16:32

Lokale Variable vs. new

Hallo Zusammen,
ich habe eine Frage zum Thema new/delete.

Damit reserviert man Speicher auf dem Heap. Aber worin liegt der Unterschied zwischen "new" und "normalen Zeigern"? "new" liefert mir die Adresse z.B. für "int" (Heap). Über einen *Zeiger kann ich an dieser Adresse einen Wert schreiben (Heap?). Also ist das im Prinzip nichts anderes, als was man zu Beginn des Themas tut: eine "int"-Variable deklarieren und definieren (Stack) und deren Adresse einem Zeiger übergeben, nur dass es eben eine Variable ist, deren Wert man einem Zeiger übergibt, während mit der "new"-Variante keine Variable, sondern lediglich die Adresse exisiert? Dabei verwirrt mich außerdem, dass es heißt mit Zeigern greift man auf den Heap zu. Wenn ich eine Variable deklariere und definiere, wird sie auf dem Stack gelegt oder nicht? Und wieso greife ich mit dem zeiger dann auf den Heap zu?

Meine zweite Frage betrifft lokale Variablen, dioe ihre Gültigkeit außerhalb eines Codeblocks verlieren und gelöscht werden. Bei "new" steht im Büch, dass die Adresse ohne "delete" weiterhin auch außerdem von Blöcken bestehen bleibt, aber warum kann ich diese nicht aufrufen?

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
int main(){
    {
        int *y=NULL;
        y=new int;
        *y=55;
        cout << "y " << y ;
    }   
    cout << "y " << *y ;

    system("Pause");
    return 0;
}

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

05.07.2017, 16:58

Mit Zeigern greift man nicht auf den Heap zu. Wer auch immer das sagt, erzählt dir Quatsch. Mit Zeigern greifst du auf Adressen zu. Diese können im Heap liegen, müssen aber nicht. Die können auch genauso gut im Stack liegen.
Heap und Stack sind verschiedene Datenbereiche. Im Stack liegen lokale Variablen und Rücksprungadressen. Dieser ist gegenüber dem Heap stark limitiert und du kannst im Stack auch nicht dynamisch entscheiden, wie viel Speicher zu haben willst. Du kannst da also keinen IF-Block machen, darin Stack-Variablen verschiedener Typen anlegen und sie außerhalb verwenden - also z.B. einen Array mit mal 4 oder mal 7 Elementen. Das geht im Stack nicht, das muss feststehen, wenn der Block betreten wird. Du darfst auch nie eine Funktion schreiben, die eine Variable im Stack erstellt und danach die Adresse zurückliefert. Is klar, weil der Stackbereich verlassen wird, wenn die Funktion beendet wird. Die einzige Möglichkeit ist also dir per 'new' einen neuen freien Bereich im Heap zurückgeben zu lassen, wo deine Variable dann drin liegt. Du bekommst dazu nur eine Adresse zurück, diese ist aber auch dann absolut noch gültig, wenn du die Funktion verlässt, in welcher du 'new' aufgerufen hast.

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
void foo()
{
   if ( soemthing )
      int[15] langerArray;
   else
      int[10] kurzerArray;
   langerOderKurzerArray[abc] = 1; // geht offenbar nicht.
}
//==============================
int* neuerInt()
{
    int x;
    return &x; // geht technisch, gibt aber im Normalfall tierisch üble Abstürze, wenn man von außerhalb auf so eine Adresse schreibt
}
void benutzNeuenInt(int* y)
{
    y += 15;
}
benutzeNeuenInt( neuerInt() ); // das hier ist plötzlich sehr ungesund

//==============================
// problemlos:
//==============================
void foo()
{
    int* meinArray;
    if ( something )
        meinArray = new int[15];
    else
        meinArray = new int[10];
    meinArray[5] = 7; // yeay
}

int* neuerInt()
{
    return new int;
}
void benutzNeuenInt(int* y)
{
    y += 15;
}
benutzeNeuenInt( neuerInt() ); // geht problemlos - auch wenn hier irgendwo jemand ein delete vergessen hat
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »BlueCobold« (05.07.2017, 17:08)


TigerClaw25

unregistriert

3

05.07.2017, 17:10

Wenn ich dich also richtig verstehe, hat folgendes Beispiel im Prinzip nichts mit dem Heap zutun:

C-/C++-Quelltext

1
2
3
int i=0;
int *pZeiger=NULL;
pZeiger = &i;


Das Thema Heap bezieht sich vielmehr auf "new"?

Nochmal kurz zu "new". Das bedeutet, ich kann auf diese Adresse, die ich von "new" zurückgeliefert bekomme, nur mit einem Zeiger zugreifen? Ich komme deshalb durcheinander, weil ich zuvor auf Werte nicht nur über Zeiger, sondern auch über die lokale Variable selbst auf Werte zugreifen konnte, was mit "new" aber nichts zutun hat.

Aber wenn nach Verlassen einer Funktion die Adresse dennoch bestehen bleibt, wieso kann ich dann nicht weiterhin darauf zugreifen?

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

05.07.2017, 17:12

'new' liefert dir einen Pointer zurück. Du kannst daraus eine Referenz machen, wenn dir das beliebt. Oder ihn dereferenzieren. Es wird aber niemals eine lokale Variable (also im Stack) werden.
Und ja, das Beispiel da hat überhaupt gar nichts mit dem Heap zu tun. Du erzeugst da einen Pointer und gibst ihm die Adresse einer lokalen Variablen.

Du kannst auch nach dem Verlassen einer Funktion auf jede beliebige Stack-Adresse zugreifen. Aber dort liegen wie ich schon sagte ja auch Rücksprungadressen. Machst du davon eine kaputt, stürzt das Programm höchst wahrscheinlich ab, weil es an eine Kauderwelsch-Adresse zurückspringt und dort die Daten als völlig unsinnigen Code interpretiert.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

TigerClaw25

unregistriert

5

05.07.2017, 17:14

Und wo liegt der Pointer in meine Fall, wenn ich die Adresse der lokalen Variablen übergebe? Auch im Stack?

Oder ich formuliere meine Frage anders. Lokale Variablen werden auf dem Stack abgelegt. Mit "new" reserviere ich eine Adresse auf dem Heap, muss nach Verlassen eines Blocks, falls nötig, den Speicher wieder freigeben.

Aber wie funktionierte das dann mit den Zeigern, die die Adresse von lokalen variablen übergeben bekommen haben? Werden diese auch auf dem Stack abgelegt?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »TigerClaw25« (05.07.2017, 17:21)


TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

6

05.07.2017, 17:27

kannst im Stack auch nicht dynamisch entscheiden, wie viel Speicher zu haben willst.
Ich moechte hier noch erwaehnen, das die nur eine Einschraenkung der Sprache ist. Im Grund ist der Stack auch einfach nur ein Stueck normaler Speicher und der Stackpointer ein normaler Pointer und ich kann den jederzeit um einen beliebigen Betrag verschieben, auch beliebig dynamisch, er muss halt nur innerhalb des Speicherbereichs bleiben.


Und wo liegt der Pointer in meine Fall
Meinst du wirklich den Pointer oder nicht vielmehr die Daten, auf die der Pointer zeigt?

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

7

05.07.2017, 17:31

Du musst unterscheiden zwischen dem Zeiger an sich und dem, worauf er zeigt. Ein Zeiger kann eine lokale Variable sein und daher im Stack liegen. Ein Zeiger kann aber auch auf dem Heap liegen. Wo der Zeiger liegt, und wo das liegt, worauf er zeigt, sind zwei völlig unabhängige Dinge.

Beispiel für Zeiger auf dem Stack:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
int* test()
{
    int* data; // data ist ein Zeiger, der auf dem Stack liegt.
    data = new int[100]; // data zeigt jetzt auf die Startadresse von 100 ints im Heap.
    return data;

    // Nach Verlassen der Funktion ist der Zeiger weg (er war ja auf dem Stack),
    // aber der Speicherbereich auf dem Heap, worauf er gezeigt hat, ist noch reserviert und auch noch gültig.
    // Hoffentlich merkt sich jemand (der, der die Funktion aufgerufen hat) die Adresse,
    // um den Speicher später freigeben zu können.
    // Das ist übrigens schlechter Stil, aber soll ja nur ein Beispiel sein.
}

Beispiel für Zeiger auf dem Heap:

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
struct Pelle
{
    bool ausEchtemDarm;
};

struct Wurst
{
    Pelle* pelle;
};

void test2()
{
    Wurst* wurst; // wurst ist ein Zeiger, der auf dem Stack liegt.
    wurst = new Wurst; // wurst zeigt jetzt auf die Adresse eines Wurst-Objekts im Heap.
    
    // wurst->pelle ist jetzt ein Zeiger, der auf dem Heap liegt.
    // Da der Zeiger nicht initialisiert wurde, zeigt er *irgendwohin*.

    Pelle pelle; // pelle liegt auf dem Stack.
    wurst->pelle = &pelle; // wurst->pelle ist jetzt ein Zeiger, der auf dem Heap liegt, aber auf den Stack zeigt.

    delete wurst;
}

Alle Klarheiten beseitigt? ;)

TigerClaw25

unregistriert

8

05.07.2017, 17:59

Ich leite davon folgendes ab.

Lokale Variablen werden auf dem Stack abgelegt. Ein deklarierter Zeiger ist dann entweder auf dem Stack oder auf dem Heap oder einfach in einem Register. Dann erfolgt die Zuweisung der Adresse.

Bei dem Befehl "new" erzeuge ich mir anders als bei der lokalen Variablen auf dem Stack einfach nur eine bestimmte Größe eines Datentyps an einer bestimmten Adresse, daher dynamisch. Wo sich der Zeiger befindet, spielt keine Rolle, da dieser sowieso nur auf die Adresse zugreift. Es geht also lediglich darum, ob die Adresse für einen Datentyp auf dem Stack liegt oder auf dem Heap, korrekt?

Bisher dachte ich, dass die Zeiger an sich immer auf dem Heap sind, weil dadurch das Problem mit der Parameterübergabe von Funktionen bei großen Objekten umgangen wird, aber die Zuweisung einer Adresse an sich hat mit Heap/Stack nichts zutun, so viel hab ich bereits verstanden.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

9

05.07.2017, 18:01

Denke nicht in Registern, wenn es um C++ geht. Der Pointer ist entweder eine lokale Variable, eine Variable auf dem Heap oder ein temporärer Wert (wie z.B. Rückgabewerte welche sind). Und er zeigt auf einen Speicherbereich im Stack oder im Heap. Ein Pointer ist also eine Variable, die eine Adresse enthält.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

10

05.07.2017, 18:08

Natürlich werden im Maschinencode Zeiger auch in Register geladen. Aber du hast (meistens) die Möglichkeit, mit dem &-Operator die Adresse abzufragen, quasi die "Heimatadresse", und die liegt dann entweder auf dem Stack oder auf dem Heap.

Werbeanzeige