Array und Pointer



  • Ach so, ist mir gar nicht aufgefallen.
    Trotzdem das dann als Hinweis für die Zukunft.



  • Nein, Jürgen Wolf sollte man nicht kennen. Der ist Autor von den schlechtesten C/C++* Lehrbüchern überhaupt.

    Genau deswegen frage ich ja nach. Sein Code hat eine gewisse Ähnlichkeit zu JW. Und man möchte ja vermeiden dass er einen solchen Stil anschlägt.

    @DKlay13:

    Hmm ok das kannte ich zwar aber selten bis nie verwendet, aber wieso wird mein Programmierstill runtergemacht?

    Tut mir Leid, ich möchte dich oder deinen Programmierstil nicht runtermachen sondern nur dir einige Fehler in deinem Code aufzeigen. 😞

    Wenn dein Prof. deinen neuen Code nicht akzeptiert, dann mach es halt so wie er es mag.

    1.) Speicherloch: Wer ein new aufruft, muss auch das entsprechende delete aufrufen. Besser ist da RAII oder Smart Pointer (unique_ptr,...). Es gibt kein Argument zu sagen: soll sich das Betriebsystem um die Freigabe kümmern!
    2.) Zeiger sind gemein. Sie können auf die gewünschten Daten zeigen, aber auch auf nullptr, 0xCCCCCCCC oder ähnlichem. Referenzen gefallen mir da besser.
    3.) const ist dein Freund. Schränkt deinen Code in einer schönen Art und Weise ein, und vermeidet unerwünschte Seiteneffekte wie:

    double maxAbstand(Punkt **arrPunkt, Punkt *p1, Punkt *p2)
    {
        double nMax = 0;
        double nTempCalc;
        //arrPunkt ist übergebenes Feld
        //über p1 und p2 werden die Punkte zurückgegeben welche den größten abstand haben
        //double abstand(Punkt p1, Punkt p2)
        for(int x=0; x<ARRAY_GROESSE; x++)
        {
            nTempCalc = abstand( *(arrPunkt[x]), *(arrPunkt[ARRAY_GROESSE-1-x]) );
    
            if( nMax < nTempCalc) {
                p1 = arrPunkt[x];
                p2 = arrPunkt[ARRAY_GROESSE-x-1];
                nMax = nTempCalc;
            }
        }
        // So hier bin ich gemein. Ich lösche das Punkt Array.
        DeletePointArray(arrPunkt);
        // Frage: Wohin zeigen nun p1 und p2? Und wie bemerke ich den Fehler?
        // Ist eigentlich sehr gemeiner Fall. Denn sobald jemand das Array verändert,
        // werdem deine Zeiger oder deren Inhalt p1 und p2 ungültig. (Punkt einfügen)
        // Ich glaube hier wäre es besser die Punkt Call By Value zurückzugeben!
        return nMax;
    }
    

    4.)

    //double abstand(Punkt p1, Punkt p2)
    double abstand(const Punkt& p1, const Punkt& p2)  // Kein Overhead, da Call By Reference, nicht Call by Value.
    // Call by Value wäre hier vermutlich auch noch ok, da Point realtiv klein ist
    {
        return sqrt( (p2.y-p1.y)*(p2.y-p1.y) + (p2.x-p1.x)*(p2.x-p1.x) );
    }
    

    4.) Nicht ganz so wichtig: Ich bin kein Freund von Zeiger auf Zeigern. Ich habe ständig das Problem wie ich darauf zugreife:

    Punkt **arrPunkt = neuesPunktFeld(ARRAY_GROESSE);
    (*arrPunkt)[0] = Punkt;
    //oder 
    *(arrPunkt[0]) = Punkt;
    

    5.) Nicht ganz so wichtig: Um ein Array hochzählen sollte man zumindestens ein unsigned int nutzen oder besser size_t und ähnliches. Problem: Wenn die int nur 16 Bits hat, kannst du so nur 2^15 Elemente = 32768 ansprechen.

    Punkt **neuesPunktFeld(int nDim)
    {
        Punkt **arrPunkt = new Punkt*[nDim];
        //for(int i = 0; i < nDim; i++) // so finde ich es lesbarer
        for(unsigned int i = 0; i < nDim; i++) // so finde ich es lesbarer
        {
            arrPunkt[i] = neuerPunkt();
        }
        return arrPunkt;
    }
    


  • DKlay13 schrieb:

    Punkt *neuerPunkt()
    {
        ...
    }
    
    Punkt **neuesPunktFeld(int nDim)
    {
    	Punkt **arrPunkt = new Punkt*[nDim];
    	for(int i=0; i<nDim; i++) {
    		arrPunkt[i] = neuerPunkt();
    	}
    	return arrPunkt;
    }
    

    Die Funktion "neuesPunktFeld" erzeugt kein Feld aus Punkten sondern ein Feld aus Zeigern auf Punkten. Es ist also nicht nur gelogen sondern auch ungeschickt.

    In gutem, modernem C++ würde das eher so aussehen:

    Punkt neuerPunkt() // <-- kein Zeiger als Rückgabewert
    {
        ...            // <-- kein new
    }
    
    vector<Punkt> neuesPunktFeld(int nDim) // <-- kein Zeiger als Rückgabewert
    {
        vector<Punkt> v;                   //     hier reserviert der vector für
        v.reserve(nDim);                   // <-- uns den Speicher und kümmert
        for (int i=0; i<nDim; ++i) {       //     sich dann selbst um die Freigabe
            v.push_back(neuerPunkt());
        }
        // Das reserve ist optional. push_back kann von sich aus auch den
        // vector vergrößern. Aber hier wissen wir schon die finale Größe
        // und können es dem vector per reserve mitteilen. Das wird dann
        // ein kleines bisschen effizienter so.
        return v;
    }
    

    Man kann das auch noch etwas generischer machen, so dass die Funktion neuesPunktFeld eine nahezu beliebige schon existierende Datenstruktur befüllen kann. Aber das wäre hier wahrscheinlich Overkill.

    Fazit: Nimm das, was man Dir da beibringen will, nicht für bare Münze. Möglicherweise kennt Dein Lehrer auch nicht die Größe seiner Wissenslücken; denn sonst könnte er Euch nicht guten Gewissens so einen schlechten Stil beibringen. typedef struct und Doppel-Zeiger ohne eine Spur von RAII sind leider Warnhinweise bzgl. Inkompetenz. Ich empfehle Dir ein begleitendes Selbststudium basierend auf 'nem schlauen Buch, was einen guten Ruf genießt. Achtung: guter Ruf != positive Amazon-Bewertungen. Ein Anfänger, der keine Ahnung hat, kann ein Buch höchstens hinsichtlich Schreibstil bewerten jedoch nicht die Qualität des technischen Inhalts.



  • Danke nochmal für die Tips werde ich mir zu Herzen nehmen 👍 👍

    Aber das mit dem doppelt Pointer würde mich trotzdem interessieren auch wenn es schlechter Still ist.


Anmelden zum Antworten