Mit Zeiger auf Zeiger rechnen



  • supertux schrieb:

    I'm on point schrieb:

    denk' lieber drüber nach, was das bewirkt

    char vek[8]="Hallo";
    char *pvek=&vek; <--- diese zeile
    

    🙂

    das ist nicht der einzige Fehler 😉

    es ist ja nicht direkt ein fehler, nur macht es nicht das, was der OP vielleicht meinte.
    🙂



  • Stimmt.

    *pvek ist ein Zeiger auf vek[0]. Ich brach also 8 Zeiger, die auf die einzelnen Elemente zeigen.

    Ich würde sagen die Syntax müsste so aussehen:

    #include <stdio.h>
    
    int main()
    {
        char vek[8]="Hallo";
        char *pvek[8]=&vek[];
    }
    

    Sytax falsch.

    Eine andere Möglichkeit wäre ja, in der Funktion den Zeiger der mainfunktion zu verändern. Ich übergebe also den Zeiger...

    #include <stdio.h>
    
    int main()
    {
        char vek[8]="Hallo";
        char *pvek=&vek;
        funk(pvek);
        printf("%s",vek);
    }
    
    int funk(char **ppvek)
    {
       strcpy(*ppvek, "funzt");
    }
    

    ... und zeige in der Funktion auf den Zeiger. strcpy soll jetzt die Elemente "funzt" dahinkopieren wo der Zeiger pvek hinzeigt (pvek[0]...pvek[7])... stürzt ab.



  • es reicht natürlich eine indirektion, aber wenn du wieder über doppelpointer (char**) gehen willst, dann fehlt dir noch ein richtiger char*. das array selber kannste dafür nicht nehmen.
    acht pointer brauchste aber definitiv nicht.
    🙂



  • dahaack schrieb:

    Stimmt.

    *pvek ist ein Zeiger auf vek[0]. Ich brach also 8 Zeiger, die auf die einzelnen Elemente zeigen.

    hä? 😕

    du solltest lieber nochmal das Kapitel mit den Zeigern wiederholen...

    #include <stdio.h>
    #include <string.h>
    
    int funk(char **ppvek);
    
    int main()
    {
        char vek[8]="Hallo";
        char *pvek=vek;
        funk(&pvek);
        printf("%s",vek);
    }
    
    int funk(char **ppvek)
    {
       strcpy(*ppvek, "funzt");
    }
    

    Ich sehe aber hier keinerlei Verwendung für Doppelzeiger, deswegen ist dieses Beispiel sinnlos. Doppelzeiger muss man verwenden, wenn man z.B. über eine Funktion die Adresse ändern muss, worauf ein Zeiger zeigt:

    int concat_string(char **str, const char *s)
    {
        char *tmp;
        size_t len_new;
    
        len_new = strlen(*str) + strlen(s) + 1;
    
        tmp = realloc(*str, len_new);
    
        if(tmp == NULL) return 0;
    
        /* realloc kann die Adresse geändert haben,
           wir müssen also den Inhalt des Zeigers ändern */
    
        *str = tmp;
    
        strcat(*str, s);
    
        return 1;
    }
    
    void foo()
    {
        char *x;
    
        x = malloc(8);
    
        if(x == NULL) return;
    
        strcpy(x, "Hello, ");
    
        if(concat_string(&x, "World!"))
            printf("%s\n", x);
    
        free(x);
    }
    


  • hä? 😕

    du solltest lieber nochmal das Kapitel mit den Zeigern wiederholen...

    Ja ach, rate mal was ich hier gerade versuche. Ich hab einfach die Logik der Syntax für Zeiger noch nicht gecheckt. Einige sagen Zeiger und Vektoren sind das gleiche, andere sagen Zeiger und Vektoren sind ähnlich. Wenn man ein & vor einer Variable schreibt erhält man die Adresse. Das & muss man bei einem Vektor widerrum nicht mitschreiben (warum weiß glaub ich kein Mensch).

    Ich will wissen wie dieses Programm funktioniert, für den Fall, dass ich einen Vektor habe, diesen einer Funktion übergebe und von drt aus wieder einer anderen Funktion übergebe. Dabei soll man auch in der letzten Funktion die Werte verändern können.

    Kann mir das bitte bitte jemand erklären?



  • dahaack schrieb:

    Ja ach, rate mal was ich hier gerade versuche. Ich hab einfach die Logik der Syntax für Zeiger noch nicht gecheckt. Einige sagen Zeiger und Vektoren sind das gleiche, andere sagen Zeiger und Vektoren sind ähnlich. Wenn man ein & vor einer Variable schreibt erhält man die Adresse. Das & muss man bei einem Vektor widerrum nicht mitschreiben (warum weiß glaub ich kein Mensch).
    ...
    Kann mir das bitte bitte jemand erklären?

    Also, ich versuch's.

    Leute, die sagen, dass Zeiger und Vektoren (sind Arrays damit gemeint?) gleich sind, sind falsch. Zeiger und Arrays sind 2 verschiedene Sachen.

    Ein Zeiger ist eine Variable, die einen nicht negativen Wert speichert. Dieser Wert entspricht einer Adresse des Speichers. Jede Variable (und Funktion) hat im Speicher eine eindeutige Adresse. Ist sie dir bekannt, dann kannst du auf ihre Daten zugreifen, ohne die Variable an sich zu kennen. Bsp:

    void bar(int *x)
    {
      *x = *x + 1;
    }
    
    void foo()
    {
        int y = 8;
        bar(&y);
        /* y ist hier 9 */
    }
    

    So, foo deklariert die Variable 'y' und sagen wir mal, dass 'y' sich auf der Adresse 0x4 befindet. Die Adresse 0x4 wird dann mit dem Wert 8 initialisiert. foo ruft dann bar auf. Bar hat aber keine Ahnung, dass eine Variable 'y' auf 0x4 existiert. bar bekommt jedoch einen Zeiger auf eine Integer-Variable, die auf 'y' zeigt. Das bedeutet, dass der tatsächliche Inhalt von 'x' 0x4 ist.

    'x' ist eine Variable, die sich beispielsweise auf 0x10 sein kann. Sie speichert den Wert 0x4. Damit kennt bar die Adresse von 'y' und kann den Inhalt der Adresse 0x4 ändern.

    Also, wenn du das &-Zeichen vor einer Variable setzt, gibst du die Adresse von diese Variable aus. Also ist &y 0x4. Wenn man ein *-Zeichen vor einem Zeiger setzt, dann deferenziert man den Zeiger, das bedeutet, dass man auf den Inhalt der Adresse zugreifet, worauf der Zeiger zeigt. D.h. in der Funktion bar ist 'x' zwar 0x4 aber '*x' 8, da der Inhalt auf Adresse 0x4 nämlich 8 ist.

    So, ein Zeiger ist also eine Variable, die eine Adresse speichert. Das Programm belegt/reserviert für jeden Zeiger immer die gleiche Anzahl von Bytes (4 bei x86 Rechnern), denn 32 Bits genügen, um die Adressen des Speichers darzustellen. Das heißt sizeof(int*) == sizeof(char*) == sizeof(wasauchimmer*).

    Wenn du das verstanden hast, dann ist es dasselbe mit den Doppel-, Dreifachzeiger, usw. Am Anfang wirst du dir denken, was das alles sein soll. Am Anfang ergibt das alles keinen Sinn, warum man auf einen Zeiger zeigen will. Ein Beispiel dafür ist meine concat_string Funktion beim letzten Post. Irgendwann, wenn du mehr Erfahrung mit einfachen Zeigern hast, wirst du von alleine verstehen, dass zweifache Zeiger genauso arbeiten als einfache Zeiger bloß nur eine "Etage" höher, d.h. über die Adresse einer Variable, die ebenfalls eine andere Adresse hat, kann man auf den ursprünglichen Inhalt kommen.

    Eine Variable alleine kann nur einen einzigen Wert speichern. Wenn du mehrere Werte gleichzeitig speichern willst, dann musst du ein Feld (Vektor) erzeugen.

    int feld[8];
    

    Das erzeugt ein Feld der Dimension 8 von Typ int. Das bedeutet, dass du 8 integers "hintereinanader" duch die Indizes 0 bis 7 speichern kannst. Das Programm belegt/reserviert in diesem Fall 8*sizeof(int) Bytes für das gesamte Feld. Das ist der erste große Unterschied zwischen einem Feld und einem Zeiger.

    void kracht_nicht()
    {
        int feld[4];
    
        feld[2] = 4;
    }
    
    void kracht()
    {
        int *zeiger;
    
        zeiger[2] = 4;
    }
    

    In der ersten Funktion wird 4*sizeof(int) Bytes angelegt. Der Inhalt aller Elemente ist beliebig; feld[2] wird dann mit 4 belegt. Das geht ohne Probleme, weil feld[2] auf den Speicher landet, wo das dritte Element des Feldes gespeichert wird.

    Bei der zweiten Funktion hingegen wird ein Zeiger deklariert aber nicht initialisiert. Der zeigt also irgendwo, kann sogar auf eine Stelle zeigen, wo wir weder Lese- noch Schreibzugrif haben. In diesem Fall ist es klar, dass zeiger[2] dazu führt, dass das Programm abstürzt.

    Wie könnte ich das Problem lösen? Indem ich Spiecher dafür reserviere:

    void loesung_1()
    {
        int feld[4];
        int *zeiger = feld;
        /* Alternativ zeiger = &(f[0]) */
    
        zeiger[2] = 4;
    }
    
    void loesung_2()
    {
        int *zeiger = malloc(4 * sizeof(int));
    
        if(zeiger == NULL) return;
    
        zeiger[2] = 4;
        ...
        free(zeiger);
    }
    

    Wie du siehst, kannst du ein Feld direkt einem Zeiger zuweisen. Das funktioniert, weil 'feld' (ohne die eckigen Klammern) bereits die Adresse des ersten Elements des Feldes zurückgibt.

    Wie du siehst, kann man Zeiger so behandeln, als wären sie Arrays (z.B. die [index] Notation). Aber du hast auch gesehen, dass sie trotzdem unterschiedlich sind. Ein Feld kann man aber nicht wie einen Zeiger behandeln, denn dazu müsste man genau wissen, wie groß der Speicherblock ist, worauf der Zeiger zeigt und das ist nicht möglich. Demnach sind folgende Bsp. Syntax Fehler

    int f1[4] = {9, 3, 4, 69};
    int *p = f1;
    int f2[4] = p; /* geht nicht! */
    
    /* Einzige Ausnahme, die ich kenne */
    char string[255] = "Hallo"; 
    /* aber das geht sicher nicht */
    char *str = "Hallo";
    char str2[25] = str; /* geht sicher nicht! */
    

    Ich hoffe, das beantwortet deine Frage, denn hier spielt keine Rolle, ob es sich um einen int-Zeiger oder char-Zeiger oder void-Zeiger handelt. Alle arbeiten gleich.



  • Wow, das war echt einleuchtend. Vielen Dank, dass du dir soviel Zeit für mich genommen hast! Falls ich nochmal Probleme hab meld ich mich 😃 Danke nochmal!

    Gruß Daniel



  • #include <stdio.h>
    
    int main()
    {
    	char vek[8];
    	char *pvek=vek;
    	strcpy(vek, "Hallo");
    	funk(pvek);
    	printf("%s", vek);
    
    }
    
    int funk(char *kopvek)
    {
      strcpy(kopvek, "funzt");
    }
    

    Es funzt! Danke!



  • Nur augensheinlich. Du versuchst 11 Zeichen in ein Array zu kopieren das nur für 8 Zeichen Platz hat.



  • supertux schrieb:

    Also, ich versuch's....

    der beitrag muss in die FAQ section!
    🙂



  • Tim schrieb:

    Nur augensheinlich. Du versuchst 11 Zeichen in ein Array zu kopieren das nur für 8 Zeichen Platz hat.

    doch doch, er benutzt strcpy und nicht strcat! und da strlen("funz") < 7 dann geht alles wunderbar.



  • supertux schrieb:

    Tim schrieb:

    Nur augensheinlich. Du versuchst 11 Zeichen in ein Array zu kopieren das nur für 8 Zeichen Platz hat.

    doch doch, er benutzt strcpy und nicht strcat! und da strlen("funz") < 7 dann geht alles wunderbar.

    Oh, stimmt. Irgendwie sah ich da ein strcat() wo keines war 🤡



  • ExMitglied schrieb:

    supertux schrieb:

    Also, ich versuch's....

    der beitrag muss in die FAQ section!
    🙂

    Seh ich auch so.

    Ich hab noch eine Frage zum Verständnis; endlich hab ich auch den Zusammenhang zwischen Vektoren und Pointern erkannt und habe daszu noch eine Frage, bzw. eher eine Aussage, wozu ich noch die Antwort brauche ob es stimmt oder nicht.

    Aussage: Ein Feld[8] ist nichts anderes als ein Pointer Namens Feld, der auf eine Namenlose Variable zeigt. Hinter dieser Variablen gibt es noch 7 weitere Variablen die ebenfalls keinen Namen haben. Diese können mit der Syntax Feld[i] angesprochen werden.

    2. Frage: Mit Pointern heißt es ja kann der Rechner schneller rechnen. Kann man somit auch mit Feldern schneller rechnen, da die nach meienr Definition ja bereits durch Pointer angesprchen werden.

    3. Frage: Ganz grob: Wie viel schneller kann ein Computer etwa mit Pointern rechnen als mit Variablen?



  • deine Ausage 1 ist vielleicht nicht ganz zutreffend, aber so kann man es sich ungefähr vorstellen.

    Frage 2: das kann man so nicht sagen. Wenn ich einen Zeiger auf einen Integer habe, ist es genauso schnell, denn das Kopieren des Inhalts des Intergers oder der Adresse der Variable genauso lange braucht (beide sind 32 Bit breit). Es macht nur einen Unterschied, wenn du z.B. ein struct übergibst: Sagen wir mal, dein struct braucht 1024 Bytes, dann muss das Programm 1024 Bytes übergeben. Wenn du nur den Zeiger lieferst, dann benötigst du nur 4 Bytes. Die Geschwindigkeit unetrscheidet sich vielleicht nur im Mirkosekunden bereich, nur der Speicher, den du für die Übergabe benötigst, ist kleiner.

    Frage 3: siehe 2. Zeiger sind kein Allheilmittel, man setzt nur dort Zeiger ein, wo man sie braucht.

    Ergänzung zu Frage 2:
    In C++ kann es durchaus einen Unterschied machen, wenn ich mich nicht irre, denn wenn du z.b. eine Klasse hast, die sehr lange braucht für die Ausführung des Kopierkonstruktors, dann ist die Übergabe über einen Zeiger eher schneller, da der Kopierkonstruktor nicht ausgeführt wird. Aber da ich kein C++ "Experte" bin, bin ich da nicht so sicher. In C ist die Aussage eher nicht zutreffend.



  • dahaack schrieb:

    Aussage: Ein Feld[8] ist nichts anderes als ein Pointer Namens Feld, der auf eine Namenlose Variable zeigt. Hinter dieser Variablen gibt es noch 7 weitere Variablen die ebenfalls keinen Namen haben. Diese können mit der Syntax Feld angesprochen werden.

    nö, ein feld[8] ist [i]kein* pointer, sondern eine variable bestehend aus 8 elementen des gleichen typs. wenn du allerdings diese variable z.b. an eine funktion übergibst, dann nimmt C automatisch die adresse des ersten elements (&feld[0]) und im funktionskopf ist ein pointer definiert, dem diese adresse beim aufruf zugewisen wird. pointer und arrays haben zwar schon eine gewisse verwandtschaft, sie sind aber nicht dasselbe.

    dahaack schrieb:

    2. Frage: Mit Pointern heißt es ja kann der Rechner schneller rechnen. Kann man somit auch mit Feldern schneller rechnen, da die nach meienr Definition ja bereits durch Pointer angesprchen werden.

    naja, jedenfalls schneller, als wenn er das ganze array kopieren würde. der zugriff auf array-elemente kann etwas langsamer sein als der zugriff auf einzelne variablen, weil zur laufzeit manchmal noch die entgültige adresse berechnet werden muss (was ja bei 'single' variablen wegfällt). aber das ist alles extrem vom code und vom compiler abhängig.

    dahaack schrieb:

    3. Frage: Ganz grob: Wie viel schneller kann ein Computer etwa mit Pointern rechnen als mit Variablen?

    falls du damit pointerarithmetik meinst: das kommt darauf an, wie breit deine pointer sind und wie der adressraum aufgebaut ist (lücken? segmentierung?) im idealfall ist pointerarithmetik ebenso schnell wie berechnungen mit normalen variablen.
    falls du damit kopieroperationen beim funktionsaufruf meinst: klar ist das kopieren eines pointers wesentlich schneller als das kopieren von z.b. 1MB an daten.
    🙂



  • Alles klar ich glaub ich hab jetzt alles gecheckt. Danke!!!


Anmelden zum Antworten