Tausch zweier zweidimensionaler Int-Felder durch Zeiger klappt nicht



  • Hallo Forum

    **
    Problembeschreibung:
    Ich versuche die Inhalte zweier 2dim-Int-Felder, die dyn. besorgt werden, zu tauschen. Dies funktioniert jedoch nicht.
    Der Tausch geschieht in der Funktion next_generation().
    Es gibt 4 Ausgaben der Felder: Vor und nach dem next_generation()-Aufruf und
    innerhalb der Funktion nochmal vor u. nach dem Zeigertausch.
    "Innere" Ausgaben zeigen, dass Felder getauscht sind. Verlässt er die Funktionen ist dies nicht mehr der Fall... warum... 😕 Bitte um Hilfe..
    Kompletter Code und Konsolenausgabe ist unten angehängt. Probiert ihn ruhig selbst aus falls nötig.
    **

    Programmbeschreibung:
    +Zwei 2dim-Int-Felder werden durch new_int_matrix() dynamisch reserviert und mit 0 initialisiert.
    +Feld1 wird mit ein paar werten belegt.
    +Beide Felder werden vor next_generation()-Aufruf ausgegeben.
    +next_generation() wird Aufgerufen. Darin passiert folgendes:
    ++Feld2 wird mit ein paar werten belegt.
    ++Beide Felder werden ausgegeben.
    ++Es erfolgt ein Zeigertausch mit Hilfe eines Hilfszeigers. (HIER FEHLER?)
    ++Beide Felder werden ausgegeben.
    +Beide Felder werden nach next_generation()-Aufruf ausgegeben.
    +Die beiden 2dim-Int-Felder werden durch delete_int_matrix() wieder freigegeben.

    HAUPTPROGRAMM:

    #include <iostream>     // Fuer Ausgaben
    #include <cstdlib>      // Fuer rand() und system()
    #include <ctime>        // Fuer srand
    #include <cstdio>
    
    using namespace std;
    
    typedef unsigned int uint;
    
    // HAUPTPROGRAMM
    int main()
    {
        int rows=10, columns=10;    // Zeilen u. Spalten der Matrix
    
        // Zwei Felder anlegen
        int** feld1=0;
        int** feld2=0;
        feld1 = new_int_matrix ( rows, columns );
        feld2 = new_int_matrix ( rows, columns );
    
        // FELD1-BELEGUNG
        feld1[5][5]=1;
        feld1[5][6]=1;
        feld1[6][5]=1;
        feld1[6][6]=1;
    
        system("cls");  // Konsole saubern
    
        // FELDERAUSGABE - Vor Funktionsaufruf next_generation()
        cout << "Felder vor Funktionsaufruf:" << endl;
        for(int i=0;i<rows;i+=1) {
            for(int j=0;j<columns;j+=1) cout << " " << feld1[i][j];
            cout << "  |  ";
            for(int j=0;j<columns;j+=1) cout << " " << feld2[i][j];
            cout << endl;
        }
        cout << endl;
    
        next_generation ( feld1, feld2, columns, rows );
    
        // FELDERAUSGABE - Nach Funktionsaufruf next_generation()
        cout << "Felder nach Funktionsaufruf:" << endl;
        for(int i=0;i<rows;i+=1) {
            for(int j=0;j<columns;j+=1) cout << " " << feld1[i][j];
            cout << "  |  ";
            for(int j=0;j<columns;j+=1) cout << " " << feld2[i][j];
            cout << endl;
        }
        cout << endl;
    
        // Beide Felder freigeben
        delete_int_matrix ( feld1 );
        delete_int_matrix ( feld2 );
        return 0;
    }
    

    FUNKTIONEN:
    Benutzte Funktionen:

    // === FUNCTION ======================================================================
    // Name:        new_int_matrix
    // Description: Reserviert dynamisch eine rows*columns-grosse intMatrix.
    // Parameter:   rows ist die Zeilenanzahl der intMatrix
    //              columns ist die Spaltenanzahl der intMatrix
    // Rueckgabe:   Zeiger auf die intMatrix
    // =====================================================================================
    int** new_int_matrix ( int rows, int columns )
    {
        int i;
        int **m;
        m = new int* [rows]; // Reserviert Zeigerfeld
        *m = new int [rows*columns](); // Reserviert u. Initialisiert Datenfeld
        for ( i=1; i<rows; i+=1 ) // Setzt Zeiger des Zeigerfeldes
        m[i] = m[i-1] + columns;
        return m;
    }
    
    // === FUNCTION ======================================================================
    // Name:        delete_int_matrix
    // Description: Gibt den Speicher fuer die int-Matrix hinter m frei.
    // Parameter:   m ist der Zeiger auf intMatrix
    // Rueckgabe:
    // =====================================================================================
    void delete_int_matrix ( int **m )
    {
        delete[] *m; // delete data array
        delete[] m; // delete pointer array
    }
    
    // === FUNCTION ======================================================================
    // Name:        next_generation
    // Description: Erzeugt Inhalte für zweites Feld und tauscht diese mit dem ersten Feld
    // Parameter:   hoehe ist die Zeilenanzahl der intMatrix
    //              breite ist die Spaltenanzahl der intMatrix
    // Rueckgabe:   0
    // =====================================================================================
    uint next_generation ( int **feld1, int **feld2, uint breite, uint hoehe ){
    
        // FELD2-BELEGUNG
        feld2[1][1]=2;
        feld2[1][2]=2;
        feld2[2][1]=2;
        feld2[2][2]=2;
    
        // AUSGABE - Vor Zeigertausch
        cout << "(Innerhalb Funktion)Felder vor Zeigertausch:" << endl;
        for(uint i=0;i<hoehe;i+=1) {
            for(uint j=0;j<breite;j+=1) cout << " " << feld1[i][j];
            cout << "  |  ";
            for(uint j=0;j<breite;j+=1) cout << " " << feld2[i][j];
            cout << endl;
        }
        cout << endl;
    
        // Zeigertausch
        int** p_temp=0;
        p_temp=feld1;
        for(uint i=0;i<hoehe+1;i+=1) p_temp[i]=feld1[i];
        feld1=feld2;
        for(uint i=0;i<hoehe+1;i+=1) feld1[i]=feld2[i];
        feld2=p_temp;
        for(uint i=0;i<hoehe+1;i+=1) feld2[i]=p_temp[i];
    
        // AUSGABE - Nach Zeigertausch
        cout << "(Innerhalb Funktion)Felder nach Zeigertausch:" << endl;
        for(uint i=0;i<hoehe;i+=1) {
            for(uint j=0;j<breite;j+=1) cout << " " << feld1[i][j];
            cout << "  |  ";
            for(uint j=0;j<breite;j+=1) cout << " " << feld2[i][j];
            cout << endl;
        }
        cout << endl;
    
        return 0;
    }
    

    KONSOLENAUSGABE:

    Felder vor Funktionsaufruf:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    (Innerhalb Funktion)Felder vor Zeigertausch:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 2 2 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 2 2 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    (Innerhalb Funktion)Felder nach Zeigertausch:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 2 2 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 2 2 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 1 1 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 1 1 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    Felder nach Funktionsaufruf:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 2 2 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 2 2 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    Process returned 0 (0x0)   execution time : 0.106 s
    Press any key to continue.
    


  • Spricht was dagegen, C++ zu benutzen.



  • Hallo,

    ich habe nicht alles nachvollzogen aber der Tausch in next_generation funktioniert so nicht:

    uint next_generation ( int **feld1, int **feld2, uint breite, uint hoehe )
    {
     //...
     p_temp=feld1;
     //...
     feld1=feld2;
     //...
     feld2=p_temp;
    }
    

    Du übergibst feld1 und feld2 per Value. D.h. die Doppelzeiger feld1 und feld2 in "next_generation" sind ein Kopie von den Zeigern feld1 und feld2 in Deiner "main"-Funktion (nur die Zeiger sind eine Kopie, nicht das worauf sie zeigen.) Wegen der Kopie ist dieser Tausch nur innerhalb der Funktion next_generation sichtbar. Du musst also das, worauf feld1 und feld2 zeigen vertauschen, und das versuchst Du auch:

    uint next_generation ( int **feld1, int **feld2, uint breite, uint hoehe )
    {
     //...
    for(uint i=0;i<hoehe+1;i+=1) [i]=feld1[i];
     //..
    for(uint i=0;i<hoehe+1;i+=1) feld1[i]=feld2[i];
     //..
    for(uint i=0;i<hoehe+1;i+=1) feld2[i]=p_temp[i];
    }
    

    Das sieht zwar umständlich aus, könnte aber funktionieren, wenn p_temp auf freien gültigen Speicher zeigt. In der Kombination, wie du es geschrieben hast, hat der 2. Teil keine Wirkung denn:

    p_temp=feld1;
    for(uint i=0;i<hoehe+1;i+=1) p_temp[i]=feld1[i];
    

    p_temp ist in der Schleife schon gleich feld1. Also ist temp[i] sowieso schon gleich feld[i]. Genau so ist es auch bei den nächsten beiden Tauschversuchen.

    EDIT: "Wegen der Kopie ist dieser Tausch nur innerhalb der Funktion next_generation sichtbar." hinzugefügt.



  • Danke für den Hinweis mit den Zeiger-Kopien DJohn.
    Ich habe mir die beiden Zeiger, die ich übergebe, vorher ausgeben lassen:

    &feld1: 0x28fedc
    &feld2: 0x28fed8
    feld1:  0x3f0ff0
    feld2:  0x3f1020
    

    Und habe mir diese nochmal innerhalb der Funktion ausgeben lassen:

    &feld1: 0x28fe70
    &feld2: 0x28fe74
    feld1:  0x3f0ff0
    feld2:  0x3f1020
    

    Die Adressen sind unterschiedlich. Also Kopien wie du geschrieben hast.

    Den Code mit dem Zeigertausch:

    // Zeigertausch
        int** p_temp=0;
        p_temp=feld1;
        for(uint i=0;i<hoehe+1;i+=1) p_temp[i]=feld1[i];
        feld1=feld2;
        for(uint i=0;i<hoehe+1;i+=1) feld1[i]=feld2[i];
        feld2=p_temp;
        for(uint i=0;i<hoehe+1;i+=1) feld2[i]=p_temp[i];
    

    habe ich nun geändert in:

    // Zeigertausch
        int* p_temp=0;
        for(uint i=0;i<hoehe;i+=1) {
            p_temp     =   feld1[i];
            feld1[i] =   feld2[i];
            feld2[i] =   p_temp;
        }
    

    Er hatte tatsächlich, wegen den Zeigerkopien, nicht richtig getauscht und war auch sonst umständlich geschrieben. Das war der entscheidende Tipp. Danke nochmal.

    Die überarbeitete FUNKTION next_generation():

    // === FUNCTION ======================================================================
    // Name:        next_generation
    // Description: Erzeugt Inhalte für zweites Feld und tauscht diese mit dem ersten Feld
    // Parameter:   hoehe ist die Zeilenanzahl der intMatrix
    //              breite ist die Spaltenanzahl der intMatrix
    // Rueckgabe:   0
    // =====================================================================================
    uint next_generation ( int **feld1, int **feld2, uint breite, uint hoehe ){
    
        // FELD2-BELEGUNG
        feld2[1][1]=2;
        feld2[1][2]=2;
        feld2[2][1]=2;
        feld2[2][2]=2;
    
        // AUSGABE - Vor Zeigertausch
        cout << "(Innerhalb Funktion)Felder vor Zeigertausch:" << endl;
        for(uint i=0;i<hoehe;i+=1) {
            for(uint j=0;j<breite;j+=1) cout << " " << feld1[i][j];
            cout << "  |  ";
            for(uint j=0;j<breite;j+=1) cout << " " << feld2[i][j];
            cout << endl;
        }
        cout << endl;
    
        // Zeigertausch
        int* p_temp=0;
        for(uint i=0;i<hoehe;i+=1) {
            p_temp     =   feld1[i];
            feld1[i] =   feld2[i];
            feld2[i] =   p_temp;
        }
    
        // AUSGABE - Nach Zeigertausch
        cout << "(Innerhalb Funktion)Felder nach Zeigertausch:" << endl;
        for(uint i=0;i<hoehe;i+=1) {
            for(uint j=0;j<breite;j+=1) cout << " " << feld1[i][j];
            cout << "  |  ";
            for(uint j=0;j<breite;j+=1) cout << " " << feld2[i][j];
            cout << endl;
        }
        cout << endl;
    
        return 0;
    }
    

    und die nun korrekte KONSOLENAUSGABE:

    Felder vor Funktionsaufruf:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    (Innerhalb Funktion)Felder vor Zeigertausch:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 2 2 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 2 2 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 1 1 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    (Innerhalb Funktion)Felder nach Zeigertausch:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 2 2 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 2 2 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 1 1 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 1 1 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    Felder nach Funktionsaufruf:
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 2 2 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 2 2 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 1 1 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 1 1 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0  |   0 0 0 0 0 0 0 0 0 0
    
    Process returned 0 (0x0)   execution time : 0.109 s
    Press any key to continue.
    

    Den ersten Kommentar von spechtr habe ich nicht verstanden.



  • Schön dass es jetzt für Dich funktioniert. Die Frage ist halt, warum nutzt Du nicht die Möglichkeiten von C++? Stattdessen, sieht Dein Code so aus, als würdest Du C programmieren und nur printf durch cout und malloc durch new ersetzten. Rohe Zeiger brauchst Du in C++ nur ganz selten. Hier könntest Du stattdessen z.B. std::vector oder std::array verwenden. Für das Tauschen von 2 Variablen gibt es die fertige Funktion std::swap. Die macht auch nichts anderes als:

    temp=var1;
     var1=var2;
     var2=temp;
    

    nur das im Gegensatz zu Deinem Versuch die Parameter hier per Referenz übergeben werden, so dass der Tausch auch funktioniert. Dein int** ist eigenlich ein neuer Typ für eine 2-Dim-Matrix. Dazu hast Du dann die Funktionen new_int_matrix (Konstruktor) und delete_int_matrix (Destruktor). Das könnte man dann auch gleich in eine eigene Klasse packen.

    EDIT: Rechtschreibung



  • Hi nochmal

    Stimmt alles was du sagst. Warum nicht die Werkezuge nutzen die C++ bietet.

    Das ist eine Aufgabe aus dem Studium gewesen.
    Die beiden Funktionen new_int_matrix und delete_int_matrix waren vorgegeben. Aber gute Idee mit der Klasse!
    Ich vermute weil der Dozent uns den Umgang mit Zeigern näher bringen will, gibt er uns Übungen, wo wir ein wenig mit rohen Zeigern arbeiten sollen.
    Später in der Praxis werde ich definitiv das verwenden was mir C++ bietet.
    Aber erstmal versuche ich zu verstehen.

    Danke für die guten Tipps und Infos nochmal! 👍


Anmelden zum Antworten