Zeiger auf Zeiger vs Zeiger

Schnelle objektorientierte, kompilierende Programmiersprache.
Antworten
forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Zeiger auf Zeiger vs Zeiger

Beitrag von forumnewbie » Fr Feb 01, 2013 7:15 pm

Hi,

ich habe mehrmals einen Zeiger auf Zeiger gesehen, jedoch verstehe ich seinen Sinn noch nicht. Was kann ich mit einem doppelten Zeiger machen, was ich mit einem normalen nicht machen kann?

Ich habe ein Beispiel gemacht, wo ich den einfachen Zeiger an Funktionen als Argument übergebe. Ich konnte dort seine Adresse ändern, auf die er zeigt und auch den Inhalt dieser Adresse im Speicher.

Code: Alles auswählen

#include <stdio.h>
#include <stdlib.h>

void einfacherP(int *varP)
{
    *varP = 200;
}


void einfacherP2(int *varP)
{
    int *var2 = (int*)malloc(sizeof(int));
    *var2 = 999;
    *varP = var2;
}

void doppelterP(int **varP)
{
    //?
}


int main()
{
    int *var = NULL;
    var = (int*)malloc(sizeof(int));
    *var = 100;

    printf("Adresse, auf die var im Speicher zeigt: %p\n", var);
    printf("Inhalt dieser Adresse: %d\n\n", *var);
    einfacherP(var); //ändere den Inhalt der Adresse
    printf("Adresse, auf die var im Speicher zeigt: %p\n", var);
    printf("Inhalt dieser Adresse: %d\n\n", *var);
    einfacherP2(&var); //ändere die Adresse, auf die var im Speicher zeigt
    printf("Adresse, auf die var im Speicher zeigt: %p\n", var);
    printf("Inhalt dieser Adresse: %d\n", *var);

    return 0;
}
Ausgabe:
Adresse, auf die var im Speicher zeigt: 00390FE8
Inhalt dieser Adresse: 100

Adresse, auf die var im Speicher zeigt: 00390FE8
Inhalt dieser Adresse: 200

Adresse, auf die var im Speicher zeigt: 00391060
Inhalt dieser Adresse: 999
Ich habe oben eine Funktion void doppelterP(int **varP) deklariert. Sie hat einen doppelten Zeiger als Parameter. Was könnte diese Funktion machen, was eine andere mit einem einfachen Zeiger nicht machen kann? Wann wird ein doppelter Zeiger benutzt?

Danke!

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von cloidnerux » Fr Feb 01, 2013 7:32 pm

ich habe mehrmals einen Zeiger auf Zeiger gesehen, jedoch verstehe ich seinen Sinn noch nicht. Was kann ich mit einem doppelten Zeiger machen, was ich mit einem normalen nicht machen kann?
Wie ich schonmal erwähnt hatte, ist für deinen Computer alles nur Zahlen, der Kontext entscheidet wofür eine zahl steht.
In deinem Fall zeigen beide Pointer irgendwo in den Speicher.
Also hat es was mit Typensicherheit zu tun.
C hat Datentypen, damit dein Compiler für dich feststellen kann, ob du Mist baust.
Mit dem "doppelten" Zeiger hat es auf sich, dass er auf einen Zeiger zeigt.
Für folgende Variablen und Pointer

Code: Alles auswählen

int a;
int *pa = &a;
int **ppa = &pa;
sollte dir dein Compiler bei dieser Zuweisung:

Code: Alles auswählen

*ppa = 3;
mackern, da *ppa vom Typ (*int) ist und 3 vom Typ (int). Damit ist sichergestellt, dass du nicht eine Addresse im Speicher überschreibst.
Diese Pointer auf Pointer sind daher Notwendig, da du ja nicht nur einen Pointer auf Daten haben kannst, sondern auch Pointer als Daten. Damit brauchst du einen Pointer, der auf einen Pointer zeigen kann, der dann auf irgendwelche Daten zeigt.
Redundanz macht wiederholen unnötig.
quod erat expectandum

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von forumnewbie » Fr Feb 01, 2013 9:40 pm

Hmm, so richtig verstehe ich die Notwendigkeit von den Zeigern auf Zeiger noch nicht.

In der Funktion einfacherP2()

Code: Alles auswählen

void einfacherP2(int *varP)
{
    int *var2 = (int*)malloc(sizeof(int));
    *var2 = 999;
    *varP = var2;
}
bringt mir mein Compiler eine Warnung (habe sie beim ersten Mal nicht gesehen):
"assignment makes integer from pointer without a cast"
Und zwar in der Zeile:

Code: Alles auswählen

    *varP = var2;
Ich schreibe am besten auf, wie ich die ganzen Zuweisungen verstehe (korrigiert mich bitte, wenn etwas falsch ist):

Zeiger var bekommt in der main()-Funktion eine neue Adresse im RAM.

Code: Alles auswählen

var = (int*)malloc(sizeof(int));
Danach schreibe ich in die Stelle im RAM, auf die der Zeiger var zeigt, mithilfe von *-Operator einen neuen Wert -> 100.

Code: Alles auswählen

    *var = 100;
Dann übergebe ich an die Funktion einfacherP2() die Adresse von dem Zeiger selbst. Er liegt ja auch irgendwo im RAM.

Code: Alles auswählen

    einfacherP2(&var); 
In der Funktion einfacherP2() erstelle ich einen neuen Zeiger var2 mit einer neuen Adresse und speichere im RAM unter dieser Adresse einen neuen Wert -> 999.

So, was jetzt passiert, verstehe ich nicht ganz:
Die Zeiger-Variable var aus der main()-Funktion liegt irgendwo im Speicher.
Wenn ich jetzt an die Variable varP die Adresse von var2 übergebe und dann mithilfe von *-Operator auf die Adresse von var zugreife und dort einen neuen Wert eintrage, würde ich damit nicht die Zeiger-Variable var zerstören? (varP zeigt auf var)

Code: Alles auswählen

    *varP = var2;
Ich kann auf var danach immer noch zugreifen. (Zeiger->Variablen verstehe ich, aber Zeiger->Zeiger->... und noch *-Operator nicht wirklich).

Warum kommt in der Zeile diese Warnung? Sind das nicht zwei Zeiger - also vom selben Typ? Und warum verschwindet diese Warnung, sobald ich den Parameter als Zeiger auf Zeiger definiere:

Code: Alles auswählen

void einfacherP2(int **varP)
Danke!

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von cloidnerux » Sa Feb 02, 2013 12:25 am

Warum kommt in der Zeile diese Warnung? Sind das nicht zwei Zeiger - also vom selben Typ? Und warum verschwindet diese Warnung, sobald ich den Parameter als Zeiger auf Zeiger definiere:
Das kannst du dir selber Beantworten:

Code: Alles auswählen

void einfacherP2(int *varP)
{
    int *var2 = (int*)malloc(sizeof(int));
    *var2 = 999;
    *varP = var2;
}
int *varP hat den Datentyp (int*), wenn du also mit *varP darauf zugreifst, welchen Datentyp erhälst du?
Hmm, so richtig verstehe ich die Notwendigkeit von den Zeigern auf Zeiger noch nicht.
DU hast gelernt, dass du mit Zeigern dynamisch auf Daten im Ram zeigen lassen kannst, du kannst also dynamisch Daten annehmen, verwalten und verarbeiten.
Wenn du nun z.B eine Datenstruktur hast, bei der du eine Menge an Pointern hast, die auf Daten zeigen und du willst auf den ersten Pointer Zeigen, dann brauchst du auch einen Pointer.
Jetzt unterscheidet sich dieses Pointer, der auf einen Pointer zeigt nicht von einem "normalen" Pointer, außer dass du zwei mal Dereferenzieren musst.
Wenn man nun für Pointer auf Daten und Pointer auf Pointer den selben Typ verwenden würde, dann können Fehler entstehen, dadurch dass du vlt gerade nicht weißt, ob du auf einen Pointer zeigst oder eine Variable. Und wenn du einmal die Adresse von einem Pointer in eine zufällige geändert hast, kann alles Passieren.
Das will man natürlich verhindern und hat eben den Pointer-auf-Pointer eingeführt.
Das ist einfach nur ein Datentyp, der dazu da ist, dich vor schlimmen Fehlern zu schützen.

Und du hast es in deinem Beispiel selber gemerkt. Du hast mit &var die Addresse des Pointers an sich im Ram übergeben, deine Funktion wollte aber nur den Pointer, also den Wert des Pointers, haben, bis du die Definition geändert hast.
Redundanz macht wiederholen unnötig.
quod erat expectandum

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von Kerli » Sa Feb 02, 2013 12:52 am

Vielleicht hilft hier der Vergleich aus dem Wiki. Eine Variable ist ein Haus in dem sich etwas befindet, nämlich der Wert den diese Variable hat. Ein Zeiger ist auch nur eine spezielle Variable, also eine Haus in dem man die Hausnummer für ein anderes Haus findet. Wenn man jetzt einen einfachen Zeiger einer Funktion übergibt bekommt diese die Hausnummer und kann dort nachschauen welcher Wert vorhanden ist, oder auch diesen ändern. Was jedoch nicht möglich ist, ist zum Beispiel das Haus zu vergrößern um Platz für mehr Daten zu haben. Dazu braucht man einen doppelten Zeiger. Man übergibt dann also die Adresse auf eine Haus, in dem man die Adresse erfährt wo das Haus mit den eigentlichen Daten steht. Jetzt ist es möglich ein größeres Haus zu nehmen, dort die Daten hineinstecken und in dem Haus mit der an die Funktion übergebenen Adresse, die Adresse des neuen Hauses bekannt zu geben. Wenn jetzt später jemand anderes kommt und die Daten holen möchte, geht er zuerst zum Haus mit der Adresse des Doppelzeigers und erfährt dann erst dort wo er die eigentlichen Daten findet.

Dazu noch ein kurzes Beispiel:

Code: Alles auswählen

void modifyMyMemory(char** my_data)
{
  free(*my_data);
  *my_data = (char*)malloc(2*sizeof(char));
  (*my_data)[0] = 'a';
  (*my_data)[1] = 't';
}
// ...
char* data = 0;
modifyMyData(&data);
printf("data: %c %c\n", data[0], data[1]);
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von forumnewbie » Sa Feb 02, 2013 6:33 pm

Danke!

Kann ich das so zusammenfassen?:
Wenn ich den Inhalt einer Adresse ändern möchte auf die ein Zeiger in einer Funktion zeigt z.B. in f1(), ich aber diese Änderung in einer anderen Funktion z.B. in f2() durchführen will, dann muss ich an diese Funktion f2() die Adresse des Zeigers aus der Funktion f1() übergeben. Und der Parameter, an den die Adresse des Zeigers übergeben wird, muss ein "doppelter" Zeiger sein -> z.B. void f2(int **pointer).


Und verstehe ich das richtig, dass ich einen Zeiger auf Zeiger umgehen kann, in dem ich mit Rückgabewerten arbeite, die einen Zeiger zurückgeben? So wie das hier in dem Tutorial gemacht wurde? http://www.proggen.org/doku.php?id=stru ... gle-linked

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von cloidnerux » Sa Feb 02, 2013 6:38 pm

Wenn ich den Inhalt einer Adresse ändern möchte auf die ein Zeiger in einer Funktion zeigt z.B. in f1(), ich aber diese Änderung in einer anderen Funktion z.B. in f2() durchführen will, dann muss ich an diese Funktion f2() die Adresse des Zeigers aus der Funktion f1() übergeben. Und der Parameter, an den die Adresse des Zeigers übergeben wird, muss ein "doppelter" Zeiger sein -> z.B. void f2(int **pointer).
Ich würde es nicht so sehr an deinem Beispiel festmachen.
Ich hab schon mehrfach gesagt, der Kontext entscheidet über eine Variable.
Frage dich also immer, was der Kontext ist. In deiner Funktion f1 soll einen Pointer auf einen Wert bekommen, damit der Wert manipuliert wird. Deine Funktion f2 soll einen Zeiger manipulieren, der auf Daten zeigt.
Und damit ergibt sich auch, dass man statt einen Pointer auf einen Pointer zu übergeben, um einen Pointer zu manipulieren, man auch einen neuen Pointer zurückgeben kann, um dann den ursprünglichen Pointer zu verändern.
Redundanz macht wiederholen unnötig.
quod erat expectandum

forumnewbie
Beiträge: 80
Registriert: Di Jan 15, 2013 9:02 pm

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von forumnewbie » Sa Feb 02, 2013 6:52 pm

Ich werde versuchen den Kontext richtig zu interpretieren.
Das Problem ist, dass ich manche Sachen aus der Programmierung erst verstanden habe, nachdem ich mehrere Beispiele dazu gemacht habe. Und auch Fehler haben mir dabei geholfen - weil ich dann gesehen habe, dass etwas so nicht funktioniert und anders gemacht werden muss.
Mit der Übung kommt das Verständnis. Ich lese eure Antworten immer sehr aufmerksam durch und oft mehrmals, aber ich verstehe leider nicht immer alles oder manchmal etwas falsch und muss deshalb nachfragen. Deshalb danke für eure Geduld :)!

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Zeiger auf Zeiger vs Zeiger

Beitrag von Xin » So Feb 03, 2013 11:13 am

Du hast ja schon einiges gehört, ich will nur kurz eins dazu fügen:
forumnewbie hat geschrieben:Hi,

ich habe mehrmals einen Zeiger auf Zeiger gesehen, jedoch verstehe ich seinen Sinn noch nicht. Was kann ich mit einem doppelten Zeiger machen, was ich mit einem normalen nicht machen kann?

Code: Alles auswählen

void einfacherP2(int *varP)
{
    int *var2 = (int*)malloc(sizeof(int));
    *var2 = 999;
    *varP = var2;
}
Das hier kannst Du mit Zeigern auf Zeigern machen, wenn Du es richtig machst. Was Du oben geschrieben hast, funktioniert ja nicht.
varP ist (int*) auf *varP, also (int) möchtest Du ein (int *) zuweisen. Das passt nicht.

Code: Alles auswählen

void zweifacherPointer(int **varP) 
{
    int *var2 = (int*)malloc(sizeof(int));
    *var2 = 999;
    *varP = var2;   // *(int **) = (int *) -> passt
}

int main(void)
{
  int * pi;

  zweifacherPointer( &pi );  // Zeiger von pi holen, Datentyp (int **) 
  free( pi );
}
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten