C++ - Pointer auf Pointer Hilfe

Tomsen1410

Mitglied
Hey,
ich habe hier eine Lern DVD von video2brain üer C++.
So jetzt hat hier der nette Herr im Video ein Tutorialvideo gezeigt, das ich nciht ganz verstanden habe :)
Nämlich geht es um Pointer.
Und um das noch zu verschönern, geht es um Pointer auf Pointer...
Ich weiß nciht wirklich wozu man das braucht und verstehe das Beispiel Programm auch nicht sehr. Vor allem bei der "eintragen" funktion mit dem Pointer auf einen Pointer.
Wäre sehr nett wenn mir einer das Schritt für Schritt erklären würde. Hier das BspProgramm:


PHP:
#include <iostream>
#include <string>
using namespace std;

// Baumstruktur für Sortierung

struct link_str {
	string *pbegriff;
	link_str *left;
	link_str *right;
};

void eintragen(link_str **pstart, string s1)
{ // einsortieren an bestehende liste
	link_str *pliste;
	pliste = *pstart; // Startwert 
	// 1. sonderfall
	if (pliste == NULL) {
		pliste = (link_str *) malloc(sizeof(link_str));
		pliste->pbegriff = new string(s1);			
		pliste->left = NULL;
		pliste->right = NULL;
		*pstart = pliste;
		return;
	}
	// suchen nach leerem Eintrag
	while (pliste != NULL) {
		if (s1 <= *(pliste->pbegriff)) { // lexiographic kleiner oder gleich
			if (pliste->left != NULL) {
				pliste = pliste->left;
			} else {
				pliste->left =  (link_str *) malloc(sizeof(link_str));
				pliste = pliste->left;
				pliste->pbegriff = new string(s1);
				pliste->left = NULL;
				pliste->right = NULL;
				pliste = NULL; // schleife zueende
			}
		} else {// lexigraphisch groesser
			if (pliste->right != NULL) {
				pliste = pliste->right;
			} else {
				pliste->right =  (link_str *) malloc(sizeof(link_str));
				pliste = pliste->right;
				pliste->pbegriff = new string(s1);
				pliste->left = NULL;
				pliste->right = NULL;
				pliste = NULL; // schleife zueende
			}
		}
	} // while 
	// ende
}

void ausgabe(link_str *pliste)
{ // rekursiv 
	if (pliste == NULL) return;
	ausgabe(pliste->left);
	cout << "Sortiert: " << *( pliste->pbegriff) << endl;
	ausgabe(pliste->right);
}

void freigeben(link_str *pliste)
{
	if (pliste == NULL) return;
	freigeben(pliste->left);
	freigeben(pliste->right);
	delete pliste->pbegriff;
	free(pliste);
}

int main()
{
	link_str *pstart = NULL; 
	link_str *pliste = NULL;
	string s1;
	// Fuellen der Liste
	do {
		getline(cin,s1,'\n');
		if (s1 != "QUIT") {
			eintragen(&pstart,s1);
		}
	} while (s1 != "QUIT");

	// AUSGABE der Liste
	ausgabe(pstart);
	// FREIGABE der Liste
	freigeben(pstart);
	pstart = NULL;
	return(0);
}
 
Hi

Ein Pointer ist generell einmal eine Speicheradresse.
Die Speicherplätze im Arbeitsspeicher sind alle durchnummeriert (ganze Zahlen, also 1, 2, 3 usw.)
die Adresse einer Variable ist dann die Nummer von ihrem "Platz".

Ein Pointer wie "link_str *pstart" ist daher in Wirklichkeit eine int-Zahl mit einer Adresse.
Ob es jetzt link_str * oder float * oder... ist entscheidet,
welche Variablenart an dieser Adresse zu finden ist.
Der Pointer selbst ist immer ein int (weil die Adress ja immer eine ganze Zahl ist,
egal ob sie die Adresse iner float-Variable oder sonstwas ist).

Mit "pstart=..." kann man dem int eine neue Adresse zuweisen,
mit "*pstart" auf die Variable dieser Adresse dann zugreifen.

So ein float-Pointer hat drei Möglichkeiten, auf was er zeigen kann:
a) auf NULL, bedeutet: Zeigt auf keine Variable.
b) auf eine existierende Variable wie "float abc;", deren Adresse man mit "&abc" bekommt.
c) oder man besorgt sich mit new eine neue Variable. Damit man auf die auch zugreifen kann
(da sie nicht wie abc einen eigenen Namen hat), kommt die Adresse in einen Pointer.


Ein häufiger Grund für Pointer: Wenn man eine Variable an eine Funktion übergibt,
diese dort geändert wird, und die Änderung auch nach der Funktion noch da sein soll.
Beim Übergeben wird nämlich eine Kopie der Variable gemacht, nur die wird geändert.
Wenn die Funktion fertig ist, ist die Kopie wieder weg,
und das Original hat noch immer den alten Wert.

Eine Lösung wäre der Returnwert.
Problem: Geht nur mit einer Variable.
Was ist, wenn zehn Übergebene geändert werden?

Bessere Lösung: Man übergibt einen Pointer auf die Variable, also die Adresse (&...).
Dann wird eine Kopie von der Adresse gamacht. Ein Original-1234 ist als Kopie aber noch immer 1234 und "zeigt" auf die selbe Speicherstelle. Die eine, unkopierte Speicherstelle.


Warum jetzt Doppelpointer?
Mit dem Übergeben von Normalpointern erreicht man also, dass Änderungen der Variable
nicht verloren gehen. In der Funktion besorgt man sich mit new aber erst eine neue Variable.
Auf einer neuen Adresse. Dh.: Nicht nur die Wertvariable selber, sondern auch die Adresse,
also der int-Wert vom Pointer wird geändert.
Deshalb noch eine Ebene weiter: Ein Pointer auf den Pointer,
damit die neue Adresse nicht nur in der Kopie vom Pointer-int gespeichert wird...

Gruß
 
Danke schonmal :D
Die Grundlagen der Pointer wusste ich schon, aber trotzdem danke :)
Möchte nun gerne noch wissen, wozu man in der Funktion einen Pointer auf einen Pointer erzeugte?
 
Also in einer Funktion verwende ich das, um im Funktionskopf einen Pointer zurückzugeben.
Oder um ein Pointer Array zu übergeben.
Also quasie so:
C++:
void blabla(int eingang, int **ausgang)
{
   int *ausgang;
   ausgang = &eingang;
}
Hoffe ich habe nichts falsches gesagt ;)
MfG
 
Zuletzt bearbeitet von einem Moderator:
Hi,

[...]Der Pointer selbst ist immer ein int (weil die Adress ja immer eine ganze Zahl ist,
egal ob sie die Adresse iner float-Variable oder sonstwas ist).[...]
<klugsch...>Ein Pointer ist nicht immer ein int, die Größe ist je nach Architektur unterschiedlich. Es kann also gut sein, dass ein "int" 32 Bit und ein Pointer 64 Bit hat. Quelle: Klick</klugsch...>
:)

Aber ansonsten eine gute und detaillierte Erklärung des Themas.

Gruß,
BK
 
Zuletzt bearbeitet:
So, oben den Rest noch dazugeschrieben.
Die Grundlagen waren nur zum besseren Verstehen ;-]

@Bk: Sicher, ein int kann unterschiedlich groß sein.
...
 
Zuletzt bearbeitet:
Ich hab das mit dem Doppelpointer jz so verstanden, das sman ihn braucht, WEIL er eine Adresse eines Pointers erhält(siehe main).
Also erställt man so einen neuen Pointer auf dem Pointer in der "main" der auf NULL steht.
Dann wird der Pointer *pliste mit dem Wert des Doppelpointers versehen.
Und da dann *pliste = NULL ist (s. main) tritt der Sonderfall ein, -->
daher wird der Pointer *pliste mit einem beliebigen freien neuen Speicherpunkt versehen, mit der Art von link_str und erhält den Wert des Strings den man angegeben hat.
Außerdem wird dann der Pointer *pstart mit dem Inhalt oder der Adresse des *plist Pointers versehen (?), weiß nicht genau. Jedenfalls ist dann der Pointer *pstart definiert, welcher auf den Pointer in der main zeigt, der auch *pstart heißt, der jz nciht mehr auf NULL steht.

Danach geht es denke ich so weiter, dass jenachdem der Eingegebene String "größer" oder "kleiner" als der des Ersten ist. Falls kleiner dann wird ein neues Objekt nach "links" erstellt, bei dem der Pointer *links des ersten Objekts zeigt, danach wird der Pointer *pliste dem neu erstelltem Objekt zugeteilt und es fängt von vorne an....
PUUH.
Hoffentlich habe ich das richtig verstanden. :)
Falls jemand die Zeit hätte den Schrott hier zu lesen ( :D ) und auszuwerten, wäre ich sehr dankbar.
:)
 
Ja, ein**- Pointer ist ein int mit einer Dresse.
An dieser Adresse findet man wieder wieder ein int mit einer Adresse (*-Pointer).
An dieser Adresse ist dann eine Variable.
...Das kann man auch drei- oder viermal verschachteln,
wenns lustig ist (macnhmal auch benötigt).


Das mit dem Wert des Oppelp. in den P.: Hm, nein.
Am Anfang gibts den *Pointer, wird auf NULL gesetzt: Also zeigt er vorerst auf keine Variable.

Und dann will man den Pointer auf etwas zeigen lassen.
Aber nicht auf eine vorhandene Variable, sondern man erzeugt sich mit new eine Neue.
Dazu würde man zunächst mal keine **Pointer brauchen, ein einfaches
C++:
p = new ...;
würde reichen.

new macht eine neue Variable (die im Code aber keinen Namen hat).
Man kann während der Programmlaufzeit beliebig viel Variablen mit new erstellen,
zusätzlich zu den fixen im Code.
Und mit dem "p=" weist man dem Pointer-int die Adresse der neuen Variable zu.

Jetzt soll das new-Zeug aber in einer Funktion passieren.
Falsche Variante:
Man übergibt den Pointer, der auf nichts zeigt.
Beim Übergeben wird eine Kopie gemacht, wie immer beim Übergeben.
Jetzt gibts in der Funktion einen anderen Pointer, der auch auf nichts zeigt,
so wie der Pointer aus main. Aber es sind verschiedene.
Dann wird dem Kopiepointer von new die Adresse der neuen Variable gegeben.
Am Ende der Funktion verschwindet der Kopiepointer mit seiner Adresse wieder.
Und das Original im main zeigt noch immer auf NULL/Nichts.

Richtige Variante:
Man übergibt nicht das Pointer-int, sondern die Adresse vom Pointer-int.
Diese Adresse wird kopiert.
Die Kopie "zeigt" aber noch immer auf das selbe Pointerint, das auf nichts zeigt...
Das selbe Pointerint wie das im main.
Und die new-Adresse geht nicht verloren.
 
Zurück