Anfänger Verständnisproblem mit Zeiger und Speicherstelle



  • Hallo zusammen,
    habe mich gestern Abend noch in Zeiger und Funktionen eingelesen. Glaube soweit auch alles verstanden. Heute aber zweifle ich etwas daran.
    In meinem Beispielprogramm lege ich einen Zeiger (*piArray) auf den das erste Element des Arrays (iArray[0]).
    In einer Schleife übergebe ich jeweils den Wert des Zeigers an die Funktion fTausche. Der Wert und die Speicherstelle sind nach der Ausgabe von *piZeiger und *a identisch. Die Speicherstelle wird bei jedem Aufruf um 4 erhöht. Wenn ich allerdings mittels sizeof(int) um den Wert der Speicherstelle zurück gehe, dann erhalte ich eine andere Speicherstelle zurück.

    Ich verstehe das gerade nicht da ich erwartete, dass der Zeiger genau die Anzahl von Speicherstellen zurück geht, die dem Datentyp entsprechen.

    Vielleicht mag jemand helfen 😕

    Speer

    #include <iostream>
    
    using namespace std;
    //void fTausche (int *a, int *b)
    void fTausche (int *a)
    {
        cout<<"Wert: "<<*a<<" Speicherstelle: "<<a<<endl;
        a-=sizeof(int);
        cout<<"Wert zurueckge. a: "<<*a<<" Speicherst.:  "<<a<<endl;
    }
    
    int main()
    {
        int iArray[] = {5, 10,4, 12};
        int *piArray;
        piArray = &iArray[0];
    
        for (int i=0; i<=3;i++)
        {
            cout<<"piArray Wert: "<<*piArray<<" Speicherstelle: "<<piArray<<endl;
            fTausche(piArray); //,piArray+1);
            piArray+=i;
        }
    
        return 0;
    }
    


  • Das schöne an Zeigern ist ja, dass du dich nicht um die Größe des Ziels kümmern musst.
    Mit a + = 1; kommst du an das nächste Element, mit a -= 1; an das vorhergehende.

    Du hast aber a-=sizeof(int); wobei sizeof(int) meist 2 oder 4 ist.
    Als steht doch da a -= 4; .

    piArray+=i; ist die kurzschreibweise für piArray = piArray + i;
    Du veränderst also piArray. Das i wird aber auch laufen weitergezählt.
    Am Anfang hast du
    i = 0; piArray = &iArray[0]; Am Ende der Schleif kommt das += 1; Dann hast du im nächsten Durchlauf:
    i = 1; piArray = &iArray[0]; Warum noch iArray[0]? Weil i beim addieren noch 0 war.
    i = 2; piArray = &iArray[1];
    i = 3; piArray = &iArray[3]; Warum iArray[3]? Weil 1+2 = 3 ist.
    jetzt ist die Schleife fertig
    i = 4; piArray = &iArray[6];

    Die Lösung für dich ist hier: piArray++; oder piArray--;
    ++piArray; oder --piArray; geht auch.



  • DirkB schrieb:

    Das schöne an Zeigern ist ja, dass du dich nicht um die Größe des Ziels kümmern musst.
    Mit a + = 1; kommst du an das nächste Element, mit a -= 1; an das vorhergehende.

    und wenn es kein nächstes Element gibt ... ? - also mußt du dich doch um die Größe des Ziels kümmern. Mit freundlichen Grüßen von buffer overflow, seg fault & co.



  • Wenn du einen Typ T und ein T* pointer; hast, dann erhöht "pointer += 1" den Wert von pointer automatisch um sizeof(T)!

    pointer += n; //Wert von Pointer um n*sizeof(T) erhöht
    pointer += sizeof(T); //Wert von Pointer um sizeof(T)*sizeof(T) erhöht
    

    @großbuchstaben
    Mit ein bißchen Leseverständnis hättest du auch gewusst was DirkB sagen wollte und warum dein Eindwand in dem Kontext blödsinn ist.



  • großbuchstaben schrieb:

    also mußt du dich doch um die Größe des Ziels kümmern.

    Das ist jetzt der Unterschied zwischen Größe und Länge 😉

    Auf die Länge wurde dadurch geachtet, dass die Schleife begrenzt ist. Bei 4 Elementen, 4x durchlaufen.

    Und der Zeiger kann hinzeigen wo er will, nur beim Zugriff über den Zeiger muss man aufpassen.



  • Danke für die Erklärungen.

    Ich möchte am Wochenende gerne den Bubblesort über Zeiger realisieren. Über eine Zwischenvaribalen wird entsprechend der Vorgaben sortiert. Ich habe ein Verständnisproblem mit der Zuweisung. Als Beispiel hier:

    for (int i=0;i<i5;i++)
    {
      int temp = 0;
      temp = zeiger;
      zeiger = zeiger[i+1];
      zeiger[i+1] = temp;
    }
    

    Das kann so doch nicht funktionieren da müsste der Compiler doch bei temp = zeiger meckern oder?
    Wahrscheinlich ist es so einfach dass ich wieder zu kompliziert denke 😞

    Speer



  • Über Zeiger? Vielleicht suchste das:

    int temp = 4711;//changed because 0 was shitty and 0815 didnt work. 
      temp = *zeiger;
      zeiger = *(zeiger+1);
      *(zeiger+1) = temp;
    


  • Hallo zusammen,
    tut mir wirklich Leid wenn ich nochmals nerve. Ich versuchte jetzt meinen Bubblesort mittels Zeigern aufzubauen.
    Hier mein Beispielprogramm:

    int main()
    {
        int iArray[] = {5, 10,4, 12};
        int *piArray;
        piArray = &iArray[0];
        int iAnzahlElemente = sizeof(iArray)/sizeof(iArray[0]);
    
    for (int i=0;i <= iAnzahlElemente;i++)
    {
        for (int j=0;j <= iAnzahlElemente;j++)
        {
            if ( *piArray < *(piArray+1))
            {
            int temp = 0;
            temp = *piArray;
            *piArray = *(piArray+1);
            *(piArray+1) = temp;
            *piArray++;
            }
        }
    }
     for (int k=0;k<iAnzahlElemente;k++)
        cout<<iArray[k]<<endl;
        return 0;
    }
    

    Die Sortierung funktioniert nur für die ersten 3 Zahlen. Die '12' bleibt immer an letzter Stelle. Wieso springt der Compiler zu früh aus der Schleife?
    Die andere Frage ist, irgendwann erreiche ich mit *(piArray+1) das Ende meines definierten Arrays. In der folgenden Speicherstelle kann nun x-beliebige Zahl liegen. Also muss doch geprüft werden ob ich einen Überlauf produzieren und falls ja, diesen entsprechend behandeln oder?

    Vielleicht hat mir nochmals jemand einen guten tip 🙂

    Speer



  • Da sind einige Fehler drin.

    Erstens laufen beide Schleifen zu weit.
    Zweitens musst du deinen Zeiger vor der inneren Schleife auf den Anfang des Arrays zurücksetzen.
    Und drittens musst du deinen Zeiger auch dann weitersetzen, wenn du nicht tauschen musst.

    *piArray++;
    

    Das hier tut übrigens nicht, was du vermutest.



  • speer schrieb:

    Die Sortierung funktioniert nur für die ersten 3 Zahlen. Die '12' bleibt immer an letzter Stelle. Wieso springt der Compiler zu früh aus der Schleife?
    Die andere Frage ist, irgendwann erreiche ich mit *(piArray+1) das Ende meines definierten Arrays. In der folgenden Speicherstelle kann nun x-beliebige Zahl liegen. Also muss doch geprüft werden ob ich einen Überlauf produzieren und falls ja, diesen entsprechend behandeln oder?

    Vielleicht hat mir nochmals jemand einen guten tip 🙂

    Ehrlich gesagt: ich habe mir den Code jetzt nicht so genau angesehen, aber ich bin mir eigentlich sicher, dass piArray irgendwann in die Wicken zeigt...der wird ja ständig erhöht...
    Und Deine Überlegung ist richtig: den Fall, dass Du hinter das Array greifst, musst Du verhindern.

    Warum bist Du von Deiner tausche() Funktion wieder abgekommen?
    Teile und herrsche! In diesem Fall: teil das Problem in Teilprobleme auf, die Du in einzelnen Funktionen löst.

    #include <iostream>
    
    // gibt den Bereich [first, last) aus.
    void druck(const int* first, const int* last){
      while(first!=last)
        std::cout << *first++ << ' ';
      std::cout << '\n';
    }
    
    // tauscht *a und *b
    void tausch(int* a, int* b){
      //....
    }
    
    // sortiert den Bereich [first, last)
    void sortier(int* first, int* last){
      //....
    }
    
    int main()
    {
      int iArray[] = {5, 10,4, 12};
      int iAnzahlElemente = sizeof(iArray)/sizeof(iArray[0]);
    
      sortier(iArray, iArray+iAnzahlElemente);
      druck(iArray, iArray+iAnzahlElemente);
    }
    


  • Hallo zusammen,
    eigentlich wollte ich heimgehen aber das Problem konnte nicht auf morgen warten 🙂
    Daher vielen Dank an alle helfenden Hände.
    Hier nun das funktionierende Programm:

    int main()
    {
        int iArray[] = {5, 10,4, 12, 2, 1, 36};
        int *piArray;
        piArray = &iArray[0];
        int iAnzahlElemente = sizeof(iArray)/sizeof(iArray[0]);
    
        //fTausche(*piArray, iAnzahlElemente);
    for (int i=0;i < iAnzahlElemente-1;i++)
    {
        for (int j=0;j < iAnzahlElemente-1;j++,piArray++)
        {
            if ( *piArray < *(piArray+1))
            {
            int temp = 0;
            temp = *piArray;
            *piArray = *(piArray+1);
            *(piArray+1) = temp;
            }
        }
        piArray-=iAnzahlElemente;
    }
     for (int k=0;k<iAnzahlElemente;k++)
        cout<<iArray[k]<<endl;
        return 0;
    }
    

    Habe versucht möglichst viel als lokale Variablen zu deklarieren. Als nächstes noch das Auseinanderzeihen zwischen Verarbeitung und Ausgabe auf dem Bildschirm.
    Vielleicht kann jemand den Code nochmals anschauen ob noch Fehler enthalten sind bzw. wo man noch schrauben kann.
    Was mich stört ist, wie Furble Wurble bereits schrieb, die laufende Erhöhung der Zeigeradresse auch außerhalb des definierten Bereichs. Denke das meinte MFK mit die Schleifen laufen zu weit. Daher schränke springe ich bereits vor dem erreichen des letzten Elements aus der Schleife.
    Habe ich das soweit richtig verstanden?

    Speer


  • Mod

    Du denkst zu sehr in Zählschleifen, gleichzeitig benutzt du aber Zeiger. for-Schleifen können so viel mehr. Und mit Zeigern selber kann man auch rechnen. Dadurch wird das Programm unnötig umständlich. So könnte das aussehen, wenn man den Algorithmus konsequent mit Zeigern durchzieht:

    #include <iostream>
    
    int main()
    {
      int array[] = {5, 10,4, 12, 2, 1, 36};
      int *begin = array, *end = array + sizeof(array)/sizeof(*array);
    
        // Ab dieser Stelle könnte man wunderbar alles als Funktion machen, mit begin und end als Parameter:
    
        for (int *p = begin; p < end - 1; ++ p)
          for (int *current = begin; current < end - 1; ++current)
            {
              if (*current < *(current + 1))
                {
                  int temp = *current;
                  *current = *(current + 1);
                  *(current + 1) = temp;
                }
            }
    
        for (int *current = begin; current < end; ++current)
          std::cout << *current << '\n';
    }
    

    Beachte auch, dass ich die ungarische Notation weg gelassen habe. In einer Sprache, in das Definieren eigener, komplexer Datentypen zum Grundwerkzeug gehört und die die Länge der Bezeichner nicht (nennenswert) einschränkt, bringt UN mehr Probleme als sie löst (sie löst nämlich gar kein Problem, macht aber welche).



  • kleiner Troll schrieb:

    @großbuchstaben
    Mit ein bißchen Leseverständnis hättest du auch gewusst was DirkB sagen wollte und warum dein Eindwand in dem Kontext blödsinn ist.

    nein, mein Einwand trifft genau den Nagel auf den Kopf. Außerdem bin ich hier nicht in einer Textverständnis-Klausur im Leistungskurs Deutsch. Daß die formale Vermischung von Zeiger und Array a la char**, char* ...[] von C in C++ übernommen wurde, halte ich nicht für ideal, aber andererseits, zumindest im Dienst der Rückwärtskompatibilität, kaum vermeidlich.


Anmelden zum Antworten