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

Fred

Supermoderator

  • »Fred« ist der Autor dieses Themas

Beiträge: 2 121

Beruf: Softwareentwickler

  • Private Nachricht senden

1

03.11.2006, 02:36

Zeiger

Ich weiß, dass meine Frage vielleicht ein bischen blöd ist, aber Wer nicht fragt der nicht gewinnt :)

Ich versteh nicht so ganz den SInn von diesen Zeigern. Bisher hab ich halt einfach mal das mit den Zeigern gemacht, aber nun habe ich begonnen zu hinterfragen: "Warum machst du das egtl." Tja und nun möchte ich das wissen.
Warum deklariere ich bspw.

C-/C++-Quelltext

1
 CGame pGame = new CGame 


Was erreiche ich damit bzw. was ist der Sinn?
Kann man nicht auch einfach ein Gameobjekt ohne Zeiger erstellen?

C-/C++-Quelltext

1
 CGame Game;

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

2

03.11.2006, 07:35

Tja,

was ist so ein Zeiger. Um mal im Bild des Artikels im FAQ zu bleiben:

Wenn ein Objekt die Seite eines Buches ist (z.B. im DIN A0 Format, also sehr gross), dann ist die Seitennummer ein Zeiger auf dieses Objekt. Dieser Zeiger erfordert selbst deutlich weniger Platz als das Objekt, zumindest in den meisten Fällen, wenn man nicht eizelne Character mit Zeigern addressieren will. Ein Zeiger ist also die Speicheraddresse eines Objekts.

Wo ist nun der Unterschied zwischen den folgenden Konstruktionen?

C-/C++-Quelltext

1
2
CObjekt mein_objekt;
CObjekt* zeiger_auf_mein_objekt = new CObjekt;


In beiden Fällen gibt es eine Seite auf der das Objekt liegt. Es gibt aber Unterschiede.

Im ersten Fall liegt das Objekt auf dem Stack und im zweiten Fall auf dem Heap. Das ist erstmal nicht so spannend, und auch nicht so wichtig, ausser man hat wirklich viele von diesen Objekten, oder sie sind schrecklich gross. Allerdings ist der Stack eine Art Kladdeblock. Seiten, die auf dem Stack liegen werden gelöscht, wenn man die Funktion verlässt, und damit sind die darauf gespeicherten Objekte verschwunden. Verlassen in dem Sinn, dass man das Ende der Funktion erreicht hat, oder sie mit einem return vorzeitig verlässt. Wenn man eine weitere Funktion aufruft, werden einfach noch ein paar Seiten oben auf den Stack gelegt und mein Objekt bleibt am Leben. Diese Seiten werden nicht herausgerissen; man vergisst nur, dass man sie mal benutzt hat, und wenn man sie später wirder braucht, werden sie recycled und überschrieben. Ein Stack ist also ein dickes Buch mit einem Faden drin, der angibt, wo man was eintragen muss. Es wird nur der Faden im Buch umgesetzt.

Ein Objekt auf dem Heap wird nicht automatisch vernichtet und seine Lebenszeit ist unabhängig von den Funktionen, in denen ich es erzeuge. Allerdings muss ich selbst darauf achten, es wieder zu vernichten, wenn ich es nicht mehr brauche. Das schlimmste, was mir hier passieren kann, ist dass ich vergesse, auf welcher Seite mein Objekt steht, weil die Seitennummer (der Zeiger) auch wieder auf dem Stack liegt.

Zeiger haben noch einen wesentlichen Vorteil: ich kann damit grosse Objekte an Funktionen übergeben, ohne sie kopieren zu müssen. Jedes Objekt hat eine Adresse (auch Zeiger selbst!) also kann ich prinzipiell auch alles als Zeiger an Funktionen übergeben. (Das ist natürlich nicht immer sinnvoll oder gewünscht, die Schlagworte hier sind Call-by-Value und Call-by-Reference)

Aber mit Zeigern auf Objekte, die auf dem Stack liegen, muss man ein wenig aufpassen, weil solche Zeiger ungültig werden können, ohne dass man das sofort merkt.

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
void bar (int* zeiger_irgendwohin)
{
  std::cout << *zeiger_irgendwohin << std::endl;
}

int* foo ()
{
  int auf_dem_stack = 5;
  bar (&auf_dem_stack);   // Zeiger ist gültig

  return &auf_dem_stack; // Zeiger wird ungültig

}

void foobar ()
{
  int anderer_wert = 7;
}

int main ()
{
  int* zeiger_aus_funktion = foo ();
  bar (zeiger_aus_funktion);
  foobar ();
  bar (zeiger_aus_funktion);

  return 0;
}


Du kannst ja mal überlegen, was hier ausgegeben wird (ohne das zu compilieren :)) Zum Glück kann der Compiler in solchen Fällen warnen, dass man die Adresse auf eine lokale Variable als Ergebnis liefern will.

Gruss,
Rainer

PS: hmm, hier fehlen noch ein paar Beispiele und Links aber ich muss jetzt wirklich zur Arbeit ;)
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

Fred

Supermoderator

  • »Fred« ist der Autor dieses Themas

Beiträge: 2 121

Beruf: Softwareentwickler

  • Private Nachricht senden

3

03.11.2006, 14:16

Wow! Danke für diese sehr umfangreiche Antwort.
->1. Zeiger werden verwendet weil sie an Funktionen übergeben werden
können
2. Weil sie kleiner als das Objekt auf dem Stack sind

Stimmt das soweit?

Bei deinem Bsp.-Code wird der Zeiger &auf_dem_Stack doch ungültig, weil eben, das Obekt auf_dem_stack nach beenden dieser Funktion ungültig gemacht wird und somit der Zeiger auf eine Adresse auf dem Stack zeigt, die es nicht mehr gibt(Auf eine Seitenzahl, aber die Seite ist rausgerissen) Der Zeiger liegt auf dem Heap, ist also nicht gelöscht. Dann dürfte das zu nem Fehler kommen, oder?


Ach ja noch ne Frage am Rande: Wie wichtig ist das überladen von Operatoren usw.? Ich verstehe es zwar aber ich sehe auch da keinen Sinn dahinter. Könnte mir vllt. mal jemand ein praktisches(Spieleprogrammierer) Beispiel geben wann es sinnvoll ist Operatoren zu überladen. Bei Elementfunktionen ist mir das einigermasen klar.

Chase

Alter Hase

Beiträge: 753

Wohnort: Nagaoka / Darmstadt / Düsseldorf

Beruf: fauler Studi

  • Private Nachricht senden

4

03.11.2006, 15:22

Zitat von »"Fred"«

Ach ja noch ne Frage am Rande: Wie wichtig ist das überladen von Operatoren usw.? Ich verstehe es zwar aber ich sehe auch da keinen Sinn dahinter.

Das ist eigentlich reine Kosmetik, alles was man mit Operatoren macht, koennte man ebensogut als Funktion implementieren. Es ist nur viel angenehmer zu schreiben:

C-/C++-Quelltext

1
CVector result = v1 + v2;

Als eine Methode dafuer zu schreiben:

C-/C++-Quelltext

1
CVector result = v1.add(v2);

Wenn du eine Klasse hast, die man tatsaechlich "addieren" kann, bietet sich das geradezu an, aber daran gebunden mit einem operator+ auch zu addieren bist du nicht.
"Have you tried turning it off and on again?"

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

5

03.11.2006, 16:04

Zitat von »"Fred"«


->1. Zeiger werden verwendet weil sie an Funktionen übergeben werden
können
2. Weil sie kleiner als das Objekt auf dem Stack sind


Das ist wohl missverständlich formuliert. Man kann sowohl Objekte als auch Zeiger auf Objekte an Funktionen übergeben. Das ist dann im Wesentlichen der Unterschied zwischen Call-By-Value und Call-By-Reference. Wenn man allerdings das Objekt komplett übergibt, wird es immer komplett kopiert. Wenn man einen Zeiger übergibt, wird nur der Zeiger kopiert.

Der Platz für das Objekt wird in beiden Fällen belegt, der Unterschied liegt also darin, wieviel jedesmal kopiert werden muss.

Zitat


Bei deinem Bsp.-Code wird der Zeiger &auf_dem_Stack doch ungültig, weil eben, das Obekt auf_dem_stack nach beenden dieser Funktion ungültig gemacht wird und somit der Zeiger auf eine Adresse auf dem Stack zeigt, die es nicht mehr gibt(Auf eine Seitenzahl, aber die Seite ist rausgerissen) Der Zeiger liegt auf dem Heap, ist also nicht gelöscht. Dann dürfte das zu nem Fehler kommen, oder?


Beim Stack ist es eben so, dass die Seiten nicht rausgerissen werden. Die Seite ist also noch da, sie wird blos nicht die ganze Zeit benutzt. Die Adresse gibt es also noch. Der Zeiger weiss selbst nicht, was mit dem Speicher passiert, auf den er zeigt.

Um das jetzt mal komplett zu machen: der Beispielcode compiliert unter VC++2005XE, es gibt aber eine (erwartete) Warnung.

Zitat von »"Compiler"«

warning C4172: returning address of local variable or temporary
Das Programm läuft auch und gibt 5 und zwei "komische" Zahlen aus. Jetzt stelle man sich mal vor, was wohl passiert, wenn man dort etwas komplexeres als einen Integer erwartet... :oops:

Der Zeiger, der aus der Funktion foo rauskommt, wird übrigends auch auf dem Stack gespeichert, weil zeiger_aus_funktion innerhalb der main Funktion eben auch eine lokale Variable ist. :)

Gruss,
Rainer
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

Fred

Supermoderator

  • »Fred« ist der Autor dieses Themas

Beiträge: 2 121

Beruf: Softwareentwickler

  • Private Nachricht senden

6

03.11.2006, 16:45

1. Nu wovon hängt es ab welchen Wert der Kompiler bei der Ausgabe der zweiten und dritten Zahl ausgibt. Der Wert auf den der Zeiger verweist ist ja nicht mehr vorhanden

2. Welchen Sinn hat der Aufruf der Funktion foobar();?

3.

Zitat von »"rklaffehn"«


Seiten, die auf dem Stack liegen werden gelöscht, wenn man die Funktion verlässt, und damit sind die darauf gespeicherten Objekte verschwunden. Verlassen in dem Sinn, dass man das Ende der Funktion erreicht hat, oder sie mit einem return vorzeitig verlässt.

Zitat von »"rklaffehn"«


Beim Stack ist es eben so, dass die Seiten nicht rausgerissen werden. Die Seite ist also noch da, sie wird blos nicht die ganze Zeit benutzt.


Werden die Daten am Ende der Funktion nun gelöscht?
Also ich hab das egtl. so verstanden. Der Wert der Variable auf_dem_Stack wird nach verlassen der foo() funktion gelöscht. Nicht aber der Zeiger der auf die Adresse verweist, die allerdings keinen Wert mehr hat[/quote]

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

7

04.11.2006, 08:36

Zitat von »"Fred"«

1. Nu wovon hängt es ab welchen Wert der Kompiler bei der Ausgabe der zweiten und dritten Zahl ausgibt. Der Wert auf den der Zeiger verweist ist ja nicht mehr vorhanden


Ich wähle hier eine andere Formulierung: der Wert ist undefiniert.

Zitat


2. Welchen Sinn hat der Aufruf der Funktion foobar();?


Ich wollte sicherstellen, das auch was auf dem Stack passiert; aber der Compiler hat mich wohl ausgetrixt, weil da sowieso schon nicht mehr der originale Wert draufsteht. Ist also nicht wirklich wichtig.

Zitat


Werden die Daten am Ende der Funktion nun gelöscht?
Also ich hab das egtl. so verstanden. Der Wert der Variable auf_dem_Stack wird nach verlassen der foo() funktion gelöscht. Nicht aber der Zeiger der auf die Adresse verweist, die allerdings keinen Wert mehr hat


Ja, aber was bedeutet löschen? Löschen heisst nur, dass der Destruktor des Objekts aufgerufen wird und dass der Speicher zur Wiederbenutzung freigegeben wird. Sonst passiert erstmal nichts, oder wenn doch, kann man sich nicht sicher drauf verlassen, was genau passiert.

Wenn man sich also einen solchen Zeiger merkt, muss man davon ausgehen, dass das Objekt dahinter undefiniert ist. Bei einem Integer führt das eben zu "komischen" Werten bei z.B. einem String (std::string) kann -- nein, wird! -- das auch einen Absturz geben.

Gruss,
Rainer
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

8

04.11.2006, 08:49

Right. Ist ähnlich wie auf der Festplatte. Wenn eine Datei gelöscht wird, werden nur die Sektoren zum beschreiben freigegeben. Solange nicht drüberkopiert wurde, steht aber noch der alte Wert an der Stelle.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Fred

Supermoderator

  • »Fred« ist der Autor dieses Themas

Beiträge: 2 121

Beruf: Softwareentwickler

  • Private Nachricht senden

9

05.11.2006, 14:48

Achso jetzt ists klar. Danke rklaffehn für dieausführlichen Antworten

Werbeanzeige