Übergeben von Zeigern an Funktionen

NaRF

NaRF

Hatifschnacke
hi,
ich starre schon ewigkeiten auf ein paar programmzeilen aus dem informatikunterricht, was mich bis jetzt aber noch nicht dazu befähigt hat, sie zu verstehen :).
das programm erstellt einen binärbaum. daten werden eingelesen und dann alphabetisch in eine baumstruktur geschrieben. jedes baum struct enthält einen "infocontainer" (speichert ein char) und 2 zeiger auf die zweige rechts und links von ihm.

ein baum mit dem inhalt unixboard sieht dann z.b. so aus:

Code:
       |---> x
 |---> u
                   |---> r
             |---> o
       |---> n
             |---> i
                         |---> d
                   |---> b
                         |---> a


hier erstmal das programm:
Code:
# include <iostream>
	//# include <conio.h>
	# include <string>
	
	using namespace std;

	struct BaumTyp {char info; BaumTyp *re, *li;};

// --------------------------------------------
	[B]void einfuegen (BaumTyp* &Baum, char Daten)[/B]
// --------------------------------------------
 {
	if (Baum == NULL)
	  {
        Baum = new BaumTyp;
	    Baum->info = Daten;
   	    Baum->li = NULL;
        Baum->re = NULL;
	  }
	else einfuegen (Daten < Baum->info ? Baum->li : Baum->re, Daten);
 }

// ---------------------------------- 
	void zeige (BaumTyp *Baum, int h)
// ----------------------------------
 {
	int i;

	if (Baum != NULL)
	  {
	    zeige (Baum->re, h+1);
	    for (i=1; i<=h; i++) cout << "      ";
	    cout << " |---> " << Baum->info << endl;
	    zeige (Baum->li, h+1);
	  }
 }

// -----------------------------------
	void LWR_Durchlauf (BaumTyp *Baum)
// -----------------------------------
 {
	if (Baum != NULL)
	  {
	    LWR_Durchlauf (Baum->li);
	    cout << Baum->info << " ";
   	    LWR_Durchlauf (Baum->re);
	  }
 }

// -----------------------------------------------------
	void rechtsaussen_anhaengen (BaumTyp *a, BaumTyp *z)
// -----------------------------------------------------
 {
	if (a->re == NULL) a->re = z;
	else rechtsaussen_anhaengen (a->re, z);
 }

// ----------------------------
	void loeschen (BaumTyp* &z)
// ----------------------------
 {
	static BaumTyp *h;

	h = z;
	if (z != NULL)
	  {
	    if (z->li == NULL) z = z->re;
	    else
	      {
	        rechtsaussen_anhaengen (z->li, z->re);
		    z = z->li;
	      }
        delete h;
	  }
 }

// -----------------------------------------------
	void suchen_und_loeschen (BaumTyp* &a, char x)
// -----------------------------------------------
 {
	if (a != NULL) if (a->info == x) loeschen (a);
		           else if (a->info > x) suchen_und_loeschen (a->li, x);
				        else suchen_und_loeschen (a->re, x);
 }

// ------------
    int main ()
// ------------
 {
	[B]BaumTyp *Baum;[/B]
	char Eingabe[20], Zeichen;
	int k, L;

	Baum = NULL;
	cout << "Gib eine Zeichenkette ein: ";
	cin >> Eingabe;
	L = strlen (Eingabe);
	for (k=0; k<L; k++) einfuegen (Baum, Eingabe[k]);
	cout << endl;
	zeige (Baum, 0);
	cout << endl;
	LWR_Durchlauf (Baum);
	cout << "\n\nWelches zeichen soll geloescht werden: ";
	cin >> Zeichen;
	suchen_und_loeschen (Baum, Zeichen);
	cout << endl;
	zeige (Baum, 0);
	cout << endl;
	LWR_Durchlauf (Baum);
	
	getchar();
	return 0;
 }

verständnisprobleme habe ich in der oberen fettgedruckten zeile. ich übergebe der funktion doch einen zeiger auf eine variable des typs BaumTyp (siehe untere fettgedruckte zeile). was hat dann das "&" in der oberen zeile, das mir afaik die adresse vom zeiger liefert, dort verloren? warum kann ich den zeiger nicht einfach wie in der funktion "zeige" übergeben?
 
Weil's ein Referenzparameter ist.

[edit]
C++ im Schulunterricht?! Hach, da wird man glatt neidisch...
[/edit]

Mfg, Lord Kefir
 
Zuletzt bearbeitet:
ich hab mir den source jetzt zwar nicht richtig angeschaut aber der unterschied zur funktion zeige ist folgendes:
bei zeige() wird der baum nicht verändert dort wird er nur ausgegeben deshalb wird hier der zeiger auf den baum übergeben.
bei einfügern() veränderst du den inhalt des Baumes - deshalb musst du eine referenz (&) übergeben.

ist hier unter kapitel 5.2 glaub ich ganz gut und ausführlich erklärt:
http://www.highscore.de/cpp/einfuehrung/funktionen.html#section2
 
vielen danke euch beiden :)

wenn ich das richtig verstanden habe führt "&" also ein doppelleben und dient:
1)um die adresse einer variable/eines pointers im speicher zu erfahren:
int x=5;
cout << &x;

2)als referenz, die zwar, ähnlich wie ein pointer, die adresse einer variable speichert, beim aufruf aber einen direkten zugriff auf die variable erlaubt und nicht wie der pointer auf deren adresse:
int y=5;
int &x=y;
cout << x; //gibt 5 aus

das ganze müsste sich aber doch auch so realisieren lassen:
int y=5;
int *x=&y;
*x=7
cout << y; //gibt 7 aus


ich muss aber dazu sagen, dass ich cremis text jetzt erstmal nur überflogen habe. ich werde mir den jetzt nochmal nen bisschen genauer zu gemüte führen :hilfe2:
 
Zuletzt bearbeitet:
die sache sieht so aus:

wenn du den pointer übergiebst wird der inahlt übergeben, dh. es wird eine kopie von dem inhalt fürs unterprogramm erstellt. wenn du nun etwas veränderst veränderst du die kopie. der speicherplatz dieser kopie wird aber bei verlassen des unterprogrammes wieder frei gegen und außerdem zeigt der pointer sowieso aufs "orginal" welches nat. genau wie vor dem funktiosaufruf aussieht.

wenn du eine referenz übergibst, übergiebst du die adresse von dem speicher auf den der zeiger zeigt, wenn du nun was veränderst, wird das "orginal" verändert. und das ist ja das was du bei einer einfügen() funktion machen willst. bei der zeigen() funktion werden die daten ja nur gelesen und deswegen wird dort der pointer übergeben.
 
cremi schrieb:
die sache sieht so aus:

wenn du den pointer übergiebst wird der inahlt übergeben, dh. es wird eine kopie von dem inhalt fürs unterprogramm erstellt. wenn du nun etwas veränderst veränderst du die kopie. der speicherplatz dieser kopie wird aber bei verlassen des unterprogrammes wieder frei gegen und außerdem zeigt der pointer sowieso aufs "orginal" welches nat. genau wie vor dem funktiosaufruf aussieht.

wenn du eine referenz übergibst, übergiebst du die adresse von dem speicher auf den der zeiger zeigt, wenn du nun was veränderst, wird das "orginal" verändert. und das ist ja das was du bei einer einfügen() funktion machen willst. bei der zeigen() funktion werden die daten ja nur gelesen und deswegen wird dort der pointer übergeben.

Code:
#include <iostream>

using namespace std;

void test(int *p)
{
cout << "\nFunktion: Inhalt Zeiger: " << p << endl;
cout << "Adresse Zeiger: " << &p << endl;


*p=8;
}

int main()
{
int y=5;
int *x=&y;
cout << "\nMain: Inhalt Zeiger: " << x;
cout << "\nAdresse Zeiger: " << &x << endl;
test(x);
cout << "y=" << y << endl;
}

ausgabe:
Code:
Main: Inhalt Zeiger: 0xbffff8c4
Adresse Zeiger: 0xbffff8c0
Funktion: Inhalt Zeiger: 0xbffff8c4
Adresse Zeiger: 0xbffff8b0
y=8

laut der ausgabe greife ich in diesem beispiel aber doch direkt auf y zu obwohl ich einen zeiger auf y an die funktion übergeben habe.
vielleicht habe ich dich falsch verstanden aber ich habe deinen beitrag jetzt so gedeutet, dass ich wenn ich einen zeiger übergebe eine kopie des zeigers übergebe, der zwar auf den gleichen wert zeigt (nicht auf die gleiche variable), den aber in einen andren speicherbereich schreibt. laut meinem beispiel wird zwar tatsächlich eine kopie des zeigers angelegt (siehe adresse zeiger). der zeiger zeigt aber immer noch auf den selben speicherbereicht (siehe inhalt zeiger). deshalb kann ich auch in der unterfunktion mit hilfe des dereferenzierungsoperators * direkt auf y zugreifen.
 
Zuletzt bearbeitet:
Mal so aus Interesse: hast Du eigentlich vor C++ mal C programmiert?! Finde ich bezogen auf Speicherverwaltung etc. ziemlich nützlich. Hat mir jedenfalls ziemlich geholfen, den ganzen Kram irgendwie in meine Birne zu kriegen ;)

Mfg, Lord Kefir
 
Lord Kefir schrieb:
Mal so aus Interesse: hast Du eigentlich vor C++ mal C programmiert?!

bei uns im infounterricht waren die übergänge zwischen c und c++ eher fließend :)

speicherverwaltung haben wir vor dem thema "structs, pointer&co" noch gar nicht gemacht. bis jetzt haben wir z.b. diverse sortier/verschlüsselungsalgorhythmen (die einfachen ;) ) implentiert, grafikprogrammierung mit ner uralten borland library :oldman gemacht (z.b. erstellen von graphen, eine populationsentwicklung simuliert, mandelbrotmenge etc.) und ein programm zum knacken der vignere-verschlüsselung (anhand der buchstabenhäufigkeit) geschrieben.

zeiger und speicherverwaltung sind also totales neuland für mich und die (meisten) andren aber langsam blick ich durch :D (hoffe ich)

btw: ich hab meinen letzten beitrag nochmal editiert, das programm erweitert und dabei noch was wichtiges endtdeckt ;)
 
NaRF schrieb:
laut der ausgabe greife ich in diesem beispiel aber doch direkt auf y zu obwohl ich einen zeiger auf y an die funktion übergeben habe.
du übergiebst nicht den zeiger sondern eine referenz - also die adresse der daten - deswegen funkts

ist ja identisch mit:
Code:
int main()
{
int y=5;

cout << endl << y;
test(&y);
cout << y << endl;
}
 
Zuletzt bearbeitet:
[Offtopic]
Was habt ihr denn für nen genialen Info Unterricht???
Wir sind schon seit bald nem Monat damit beschäftigt mit Delphi (würg) Rechtecke über den Bildschirm zu schieben *langsam am rad dreh*
[/Offtopic]
 
NaRF schrieb:
bei uns im infounterricht waren die übergänge zwischen c und c++ eher fließend :)

speicherverwaltung haben wir vor dem thema "structs, pointer&co" noch gar nicht gemacht. bis jetzt haben wir z.b. diverse sortier/verschlüsselungsalgorhythmen (die einfachen ;) ) implentiert, grafikprogrammierung mit ner uralten borland library :oldman gemacht (z.b. erstellen von graphen, eine populationsentwicklung simuliert, mandelbrotmenge etc.) und ein programm zum knacken der vignere-verschlüsselung (anhand der buchstabenhäufigkeit) geschrieben.

zeiger und speicherverwaltung sind also totales neuland für mich und die (meisten) andren aber langsam blick ich durch :D (hoffe ich)

btw: ich hab meinen letzten beitrag nochmal editiert, das programm erweitert und dabei noch was wichtiges endtdeckt ;)

Ich glaube ich liege richtig wenn ich behaupte, dass Speicherverwaltung, Zeiger etc. eigentlich immer für die größte Verwirrung sorgen... :) vor allem wenn man dann in C anfängt Zeiger auf Zeiger zu programmieren, die vielleicht auch noch auf Zeiger zeigen...
Selbst programmiere ich jetzt auch noch nicht soooo lange mit C und habe erst vor kurzem mit C++ angefangen. Mir ist jedenfalls dabei aufgefallen, dass ein bischen Erfahrung in C echt nicht schaden kann. Wollte ich halt mal so als Tipp am Rande erwähnen :D

C++ in der Schule... haut mich immer noch vom Hocker. Wir quälen uns mit Turbo Pascal 3 herum. Nichts gegen Pascal - aber unsere Version ist noch von 1987 oder so... :(

Mfg, Lord Kefir
 
Zuletzt bearbeitet:
laut der ausgabe greife ich in diesem beispiel aber doch direkt auf y zu obwohl ich einen zeiger auf y an die funktion übergeben habe.
vielleicht habe ich dich falsch verstanden aber ich habe deinen beitrag jetzt so gedeutet, dass ich wenn ich einen zeiger übergebe eine kopie des zeigers übergebe, der zwar auf den gleichen wert zeigt (nicht auf die gleiche variable), den aber in einen andren speicherbereich schreibt. laut meinem beispiel wird zwar tatsächlich eine kopie des zeigers angelegt (siehe adresse zeiger). der zeiger zeigt aber immer noch auf den selben speicherbereicht (siehe inhalt zeiger). deshalb kann ich auch in der unterfunktion mit hilfe des dereferenzierungsoperators * direkt auf y zugreifen.

Moment, hier ist *p der Wert, der übergeben wird. D.h. der Inhalt des Wertes wird kopiert (hier: 0xbffff8c4) und nicht der Wert, auf den p zeigt (hier *p bzw. 5).

Alternativ lässt sich dein Code von oben auch (etwas unschön) umschreiben zu:

Code:
// --------------------------------------------
        void einfuegen (BaumTyp** Baum, char Daten)
// --------------------------------------------
 {
        if (*Baum == NULL)
          {
        *Baum = new BaumTyp;
            (*Baum)->info = Daten;
            (*Baum)->li = NULL;
        (*Baum)->re = NULL;
          }
        else einfuegen (Daten < (*Baum)->info ? &((*Baum)->li) : &((*Baum)->re), Daten);
 }
// Aufruf nun: einfuegen (&Baum, Eingabe[k]);
 
Zuletzt bearbeitet:
h2owasser schrieb:
Alternativ lässt sich dein Code von oben auch (etwas unschön) umschreiben zu:

Code:
// --------------------------------------------
        void einfuegen (BaumTyp** Baum, char Daten)
// --------------------------------------------
 {
        if (*Baum == NULL)
          {
        *Baum = new BaumTyp;
            (*Baum)->info = Daten;
            (*Baum)->li = NULL;
        (*Baum)->re = NULL;
          }
        else einfuegen (Daten < (*Baum)->info ? &((*Baum)->li) : &((*Baum)->re), Daten);
 }
// Aufruf nun: einfuegen (&Baum, Eingabe[k]);

IMHO würd ich sagen, so ist es sogar "sauberer"
 

Ähnliche Themen

Queue für copy Script

dynamische Speicherreservierung

Problem mit Texteingabe

NagiosGrapher 1.7.1 funktioniert nicht

rsnapshot und ein Rechteproblem?

Zurück
Oben