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

1

07.10.2014, 17:52

Gültigkeitsbereich vom Heap

Hallihallo!

Ich bin ein absoluter Programmierneuling und versuche mich seit zwei Wochen an C++ über das Werk von Herrn Kalista, was bis jetzt für mich als Vollneuling das bisher mit Abstand beste Buch ist, das ich zu diesem Thema gefunden habe.

Nun bin ich in Kapitel 7.7.1 "New und Delete" angekommen. Während bisher alles recht gut geklappt hat, schaffe ich es momentan einfach nicht den "praktischen Bezug" zu diesem Statement von Herrn Kalista auf S. 204 herzustellen:

"Dieser eben[per new befehl auf dem Heap] reservierte Speicher ist nun, unabhängig von dem Gültigkeitsbereich, in dem
er reserviert wurde, überall gültig. Man braucht nur die Adresse zu übergeben, schon hat
man Zugriff darauf. Wenn diese Reservierung innerhalb einer Funktion stattfindet, so wird
der betreffende Speicher nicht automatisch nach Verlassen der Funktion gelöscht, so wie es
bei lokalen Variablen der Fall ist."

Ich interpretiere das so, dass ich diese auf dem Heap erzeugte Variable, im Gegensatz zu einer Stackvariable, außerhalb des Gültigkeitsbereiches verwenden kann, in dem sie erzeugt wurde.

Aber wie gelingt mir das in der Praxis?


#include<iostream>

using namespace std;

void test();


int main()
{
//Wie gelingt mir der Zugriff von *x in der main?
//Ohne return Befehl etc.

//x wird nicht erkannt. Wieso nicht wenn es doch auch außerhalb seines
//Gültigkeitsbereiches gültig sein soll?

*x = 100;

return 0;
}


void test()
{
int *x= new int;
}


Hoffe mir kann jemand helfen! :)

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

2

07.10.2014, 18:01

Entweder musst du die Adresse von test an die main zurückgeben:

C-/C++-Quelltext

1
2
3
4
5
6
int* test()
{
int *x= new int;

return x;
}


und in main:

C-/C++-Quelltext

1
int* ptr = test();


Oder du deklarierst eine globale Variable (was oft nicht ratsam ist, weil sie eben von überall zugreifbar ist):

C-/C++-Quelltext

1
2
3
4
5
6
int* Ptr = nullptr; // vorher mit einem ungültigen Wert belegen

void test()
{
Ptr = new int;
}


Nun kannst du in main oder auch von sonstwo in der Datei auf Ptr zugreifen. Nach einem Aufruf von test ist der Zeiger Ptr dann auch initialisiert.

Es sei gesagt, dass du für nullptr den Flag -std=c++0x mit übergeben musst. In vorherigen Versionen war das Makro NULL für die Initialisierung ungültiger Zeiger-Werte zuständig.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

3

07.10.2014, 18:11

Da hast du wohl was falsch verstanden ^^ Wenn du Variablen auf dem Stack anlegst, werden diese nach dem Verlassen des Gültigkeitsbereichs (z.B beim Verlassen einer Funktion) gelöscht. Speicher, den du auf dem Heap anlegst, bleibt solange, bis er freigegeben wird. 'new' gibt dir die Adresse des Speicherbereichs zurück, die du in einem Zeiger speichern kannst. Der Zeiger selbst ist auch nur eine Stackvariable, die nach Verlassen der Funktion gelöscht wird. (Ein Zeiger ist nur ein Mittel, um eine Adresse zu speichern und eine Möglichkeit bietet, auf den Speicherbereich, den die Adresse adressiert zuzugeifen.) Du kannst aber die Adresse aus einer Funktion zurückgeben und sie anderswo benutzten.
Hoffe, das hilft dir :D

4

07.10.2014, 18:39

Hey, vielen Dank für die schnellen Antworten! :)

Einiges davon hat meinen Programmiererhorizont schon etwas erweitert, wie die Sache mit dem int* als Rückgabetyp und der Tatsache, dass ein Zeiger auch auf dem Stack abgelegt wird. :)

Also verstehe ich das richtig?
Dieser "Vorteil" der dauerhaften Gültigkeit(natürlich bis zur bewussten Freigabe) geht nicht einher mit geringerem Schreibaufwand für den Programmierer?

Der Vorteil liegt quasi "nur" in der Möglichkeit der Erzeugung eines Arrays zur Laufzeit und der Vermeidung von zu viel Hick Hack auf dem Stack, wenn man Gültigkeitsbereiche verlassen will?

Theoretisch bräuchte man ja dann keine Erstellung auf dem Heap, da man die Variable/Klasse auch so einfach zurückgeben könnte.


#include<iostream>

using namespace std;

int* test();

int main()
{
int *z = test();

cout << *z << endl;

return 0;
}

int* test()
{
int y;

int *x= &y;

*x = 100;

return x;
}

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

5

07.10.2014, 18:43

Das wäre dann undefiniertes Verhalten, da die Variable auf die du da zeigst (y) am Ende des Scopes (also am beim Verlassen der Funktion test) zerstört wird. Es kann sein, dass der Speicher ungültig ist oder dass da nun irgendein Random Wert steht.
Und nein, ein Zeiger liegt nicht auf dem Stack.
Ein Zeiger zeigt auf eine bestimmten Speicheradresse im RAM.

Hier nochmal eine ausführliche Erklärung: http://de.wikibooks.org/wiki/C++-Program…lemente/_Zeiger
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

6

07.10.2014, 18:54

Hmmm, ja. Eigentlich müsste da wirklich irgend ein random Wert/Fehler rauskommen, da man ja die Adresse eines Speicherorts weitergibt, der beim Verlassen der Funktion zerstört wird.
Jedoch gibt die Konsole immer den richtigen Wert aus. Und das die letzten Versuche immer. ?(

Ich schlaf erstmal ne Nacht drüber^^

Danke auf jeden fall für die Antworten :)

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

7

07.10.2014, 19:02

Kompiliere mal im Release Modus (bzw. mit dem Flag -O1).
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

8

07.10.2014, 19:32

Hm, natürlich liegt ein Zeiger auch auf dem Stack :huh: (außer natürlich man legt hin wiederum auf dem Heap an, aber so ein Gefrickel ist eher unüblich ;) )

Du kannst ja mal Spaßeshalber folgendes ausprobieren :D

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
#include <iostream>
using namespace std;

int *test1()
{
    int i = 446688;
    return &i;
}

void test2()
{
    int z = 995577;
}

int main()
{
    int *t = test1();
    test2();

    cout << *t;
    cin.get();
}


Was wird wohl ausgegeben werden?

9

07.10.2014, 22:28

Zitat

Hm, natürlich liegt ein Zeiger auch auf dem Stack
Wird imho "reinkompiliert", anstelle eine Adresse im Stack nochmal nieder zu legen. ;)

Zitat

Theoretisch bräuchte man ja dann keine Erstellung auf dem Heap, da man die Variable/Klasse auch so einfach zurückgeben könnte
Könnte man. Dann kopierst du aber die Werte. Mit atomaren Werten mag das gehen. Bei eindeutigen oder geshareten (bspw. Multithreading) Instanzen von größeren Datenstrukturen ist das jedoch reichlich ungünstig.
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

10

07.10.2014, 22:36

Es kommt auch im Relase Modus 100 raus.
Aber ich versteh schon in etwa so langsam was du meinst/was es letztlich damit auf sich hat!


Und der letzte Code ist echt interessant! Im Release Modus spuckt er die zu erwartenden 40k aus, im Debug jedoch die 90k.
Und mit der Erstellung auf dem Heap ist dieses Wirr Warr beendet.

Genaue Gedanken werd ich mir morgen darüber machen.
Sonst kann ich gleich wrsl nicht mehr schlafen^^
Hatte mir auch eigentlich vorgenommen heute nicht mehr reinzuschauen, aber die Neugier war zu groß :D

Werbeanzeige