C Pointer-pointer auf pointer und so lustige Sachen

ReVo

Lieutenant
Registriert
Jan. 2006
Beiträge
567
Hallo,

es geht wieder um C. Diesmal aber vllt um nicht eine zu einfache Sache, zumindest mache ich hier oft Fehler OBWOHL ich das schon tausend mal durchgegangen bin :/

Also mit einfachen Pointer kann ich gut umgehen. Verstehe die denke ich wirklich. Deswegen sagte man mir, dass ich auch so auch kompliziertere Pointer Aufgaben können müsste (pointer-pointer auf pointer usw.). Aber da hört es bei mir oft doch auf.

Nehmen wir mal so eine nette aufgabe:

PHP:
int main() {
	int x = 5;
	int y = 7;
	int *p;
	int *r;
	int **q;

	p = &x;
	r = &y;
	q = &p;
	**q = 33;
	*q = r; //hier würde auch q = &r gehen , bzw. geht hier *q = r und oben geht das nicht!! :(
	**q = 55;

	printf("\nausgabe x: %d \n", x); //hier wird x 33
	printf("\nausgabe y: %d \n", y); //hier wird y 55

	return 0;
}

Im grunde verstehe ich das auch. Also vielleicht bin ich bei "*q = r" etwas unsicher aber sonst bin ich auch ohne Programmausführung auf die gleiche Ergebnise gekommen. Was genau macht jetzt "*q = r" ? , es zeigt auf r und r zeigt ja auf y, somit sorgt **q = 55 dafür, dass y 55 wird. Okey okey verstehe soweit. Habe es ausprobiert und weiß jetzt, dass man da anstatt "*q = r" auch "q = &r" schreiben könnte, das wäre das gleiche. Zumindest kommt das gleiche ergebnis raus. Aber da ist was komisches: Wenn also *q=r == q=&r , warum kann ich weiter oben im code statt "q = &p" nicht einfach "*q = p" schreiben? Weil dann gibt es nähmlich speicherzugriffsfehler - deswegen die Frage, warum geht das unten so?

Also versteht ihr was ich Fragen möchte? ^^ , sehe dass es etwas verwirrend ist zu lesen. Also unten im code geht das mit *etwas = variable und oben geht kein *etwas = variable, sondern nur etwas = adresse von variable.

Im Grunde zeigt das doch, dass ich das nicht komplett verstanden habe(?) - da ich immer noch nicht alles 100% verstehe. Es gibt natürlich schlimmere aufgaben, da würde ich vllt sogar Falsche ergebnise hinschreiben(es geht darum, dass ich das ohne compilieren wissen will ob das code so geht und was da als ergebnis raus kommt). Ich brauche wieder mal die gewisse *klick* "aaaah" Situation :) Könnte es jemand bitte versuchen mich dazu zu bringen? Was für Aufgaben könnte ich noch eure Meinung nach durchgehen (da selbstgemachte Aufgaben ob falsch oder nicht gerade reizvoll sind)

Danke und Gruß
 
ReVo schrieb:
warum kann ich weiter oben im code statt "q = &p" nicht einfach "*q = p" schreiben? Weil dann gibt es nähmlich speicherzugriffsfehler - deswegen die Frage, warum geht das unten so?

...weil eben q = &p nicht das Gleiche ist wie *q = p

Warum? Ganz einfach:

q ist - wie bereits erwähnt - ein Zeiger auf einen Zeiger auf einen Int-Wert, würde aber in Deinem Szenario "*q = p" nicht initialisiert sein ( "int** q;" enthält keine Initialisierung, q würde also Datenmüll enthalten - eine Adresse, die Deiner Anwendung nicht gehört).

q = &p bedeutet: kopiere die Adresse der Variable p nach q
*q = p bedeutet: kopiere die Adresse, auf die p zeigt zur Adresse, auf die q zeigt

...und genau das ruft bei Dir die Zugriffsverletzung hervor, da Du über die Dereferenzierung von q ohne vorherige gültige Zuweisung auf einen fremden Speicherbereich zugreifst.
 
Zuletzt bearbeitet:
Verdammt es ist doch komplizierter als ich dachte. Ich versuche mal paar Aufgaben zu machen damit das klarer wird. Danke - warum können das einige Leute einfach? :) Ich habe wirklich schon so oft Übungen dazu gemacht...
 
Ich habe mir angewoehnt statt
PHP:
*p = 10;
lieber
PHP:
p[0] = 10;
zu schreiben. Ist mir irgendwie eindeutiger. Wobei hier wieder die moeglichkeit besteht es mit einem Feld zu verwechseln.
Sternchen verwende ich nur fuer zeigerdeklarationen (und multiplizieren :-) )
 
ABC_Freak schrieb:

Wie wäre es mit
Code:
0[p] = 10
? ;-)

Der Dereferenzierungsoperator hat eine niedrigere Assoziativität als der Indexoperator.

Spielt z.B. hierbei eine Rolle:
Code:
*i[j]

Außerdem würde ich mich bei p[0] immer fragen, was das eigentlich soll. Widerspricht meiner Meinung nach der Grundregel, dass Code immer so explizit wie möglich geschrieben sein sollte.
 
Zuletzt bearbeitet:
was macht denn *p = 10 bzw. *p[10] ?
 
ABC_Freak schrieb:
Ich habe mir angewoehnt statt
PHP:
*p = 10;
lieber
PHP:
p[0] = 10;
zu schreiben. Ist mir irgendwie eindeutiger. Wobei hier wieder die moeglichkeit besteht es mit einem Feld zu verwechseln.
Sternchen verwende ich nur fuer zeigerdeklarationen (und multiplizieren :-) )


das ist generell eine sehr schlechte idee. denn du implizierst hier dass sich hinter p ein array verbirgt, was nicht der fall ist. würde jetz ein anderer unbedarfter programmierer an dem code was ändern könnte er auf die idee kommen auf p[1] oder so zuzugreifen. solltest du lieber bleiben lassen.
 
Der Stern vor einem Zeiger dereferenziert ihn (liefert beim Lesezugriff also den Wert von der Adresse im Speicher, auf die der Zeiger zeigt).

Die Syntax "<typisierter Zeiger>[x]" ist syntaktisch equivalent zu *(<typisierter Zeiger>+x), also bewirkt der Operator [x] angewandt auf einen typisierten Zeiger eine implizite Dereferenzierung ebendiesen Zeigers.

Dies ist auch der Grund, warum <typisierter Zeiger>[0] equivalent zu *<typisierter Zeiger> ist: <typisierter Zeiger>[0] == *(<typisierter Zeiger>+0)

<typisierter Zeiger>+0 == <typisierter Zeiger> (da 0 das neutrale Element der Addition)
ergo:
<typisierter Zeiger>[0] == *(<typisierter Zeiger>) == *<typisierter Zeiger>


Demnach bedeutet "*p[10]" unter der Annahme, dass p ein Zeiger auf int ist ("int* p"):
*(*(p+10))

Typenbetrachtung:
p --> int*
p+10 --> int*
p[10] --> *(p+10) --> int
*(*p+10)) --> nicht sinnvoll, da int kein Zeigertyp und demnach nicht weiter dereferenzierbar

---------------

*p = 10 bedeutet: kopiere den Wert 10 an die Speicheradresse, auf die p zeigt. Der Inhalt des Zeigers ändert sich dadurch nicht, sondern der Inhalt des Speichers, auf den der Zeiger zeigt.

p = 10 wiederum verändert den Inhalt des Zeigers selbst - er würde dann auf die Speicheradresse 10 zeigen und den Wert im Speicher an der Adresse, auf die p zeigt, unverändert lassen.


----------------


Ohne dieses Wissen würde ich jedem davon abraten, in seinen Programmen von Zeigern exzessiv Gebrauch zu machen.

Ich kann jedem, der der englischen Sprache mächtig ist, bezüglich diesen Themas die Vorlesung von Jerry Cain an der Stanford-Universität ans Herz legen. Die Kurse sind im Internet unter Youtube verfügbar und heißen "Programming Paradigms":

http://www.youtube.com/view_play_list?p=9D558D49CA734A02&search_query=cs107
 
Zuletzt bearbeitet:
Hallo,

danke für die tolle Erklärung und Links. Bin aber wieder bei etwas was mir immer noch nicht klar werden will. Mir wurde mal gesagt, ich solle das aufzeichen, dann sieht man das besser, aber ich komme so nie auf dem richtigen ergebnis. Hier eine lustige aufgabe:
PHP:
int main() {


	int x;
	int y;

	x = 4;
	y = 6;
	
	int *p, *q, **r;

	p = &x;
	q = p;
	r = &q;
	p = &y;
	**r = *p + 3;

	printf("\nAUSGABE von x: %d\n", x); //x = 9
	printf("\nAUSGABE von y: %d\n", y); //y = 6

	return 0;
}

Also, nach vielen vielen falschen versuchen habe ich die Lösung, aber wiegesagt musst ich erstmal kompilieren und wo ich das ergebnis hatte musste ich mein Lösungsvorschlag umändern. Wenn man das ganze aufzeichnen würde, wäre das so korrekt?:

(p = &x) p ----> x dh. *p hat den wert 4 und p hat den wert der adresse von x

(q = p) q ----> p dh. *q hat jetzt den wert von *p bzw. den wert aus der adresse von p, also &x

(r = &q) r ----> q dh. einfach r zeigt auf die adresse von q, also r hat jetzt die adresse von q (&q) und *r hat jetzt die adresse von worauf p zeigt, also von x - auch wenn ich mir dabei nicht sicher bin

(p = &y) p ---> y also jetzt müsste man quasi das ganze mit (p = &x) ignorieren, da sich jetzt für den pointer und für die anderen, da die ja auf p zeigen alles ändert. *p ist jetzt 6 und somit auch *q

(**r = *p + 3) r--->p--->x okey hier ist wirrwarr, also p zeigt aber irgendwie immer noch auf x, da ja aus x eine 9 wird. Pointer pointer heißt einfach statt ein schritt, geht man zwei. Also x wird jetzt *p + 3 und wie oben geschrieben ist *p eine 6 und dann + 3 dann x = 9


GUT, das war jetzt klasse. Aber sind fehler drinne und ich konnte es nur halbwegs zusammenbasteln, weil ja die Lösung schon kompiliert habe. Geht eine schrit-für-schritt-lösung denn eigentlich nicht viel einfach bzw. sicherer. Ich verpointe mich mal sehr oft. Zeichnen kann ich das immer noch nicht so wirklich.
 
Zurück
Oben