Wo leake ich hier memory? Dynamic array



  • @Swordfish Danke fuer den Tipp, ich werds mir mal anschauen, ich wollte mir eh noch ein Buch dazu holen. Ist leichter als sich nur durch Tutorials und Dokumentationen zu lesen fuer den Anfang



  • Das Hauptproblem mit Tutorials ist daß Du die Qualität als Anfänger nicht einschätzen kannst und Du Dir sehr wahrscheinlich Blödsinn beibringst.
    Da ein Link zu "unserem" Discord: https://discord.gg/5aGDBu2r



  • @Swordfish sagte in Wo leake ich hier memory? Dynamic array:

        double input;
    
        std::cout << "Your values:\n";
        while (std::cout << data_size + 1 << ": ", std::cin >> input) {
            double *tmp = static_cast<double*>(std::realloc(data, (data_size + 1) * sizeof(*data)));
            if (!tmp) {
                std::cerr << "Not enough memory :(\n\n";
                std::free(data);
                return EXIT_FAILURE;
            }
            data = tmp;
            data[data_size] = input;
            ++data_size;
        }
    

    ach so, daraus sollte man eher eine for-loop machen:

        std::cout << "Your values:\n";
        for (double input; std::cout << data_size + 1 << ": ", std::cin >> input; ++data_size) {
            double *tmp = static_cast<double*>(std::realloc(data, (data_size + 1) * sizeof(*data)));
            if (!tmp) {
                std::cerr << "Not enough memory :(\n\n";
                std::free(data);
                return EXIT_FAILURE;
            }
            data = tmp;
            data[data_size] = input;
        }
    

    Hat den Vorteil der engeren Lokalität von input und der hervorragenden Stelle an der data_size inkrementiert wird.



  • @marcel91200 Darf ich fragen welche Uni?



  • @Swordfish sagte in Wo leake ich hier memory? Dynamic array:

    ach so, daraus sollte man eher eine for-loop machen:

        std::cout << "Your values:\n";
        for (double input; std::cout << data_size + 1 << ": ", std::cin >> input; ++data_size) {
            double *tmp = static_cast<double*>(std::realloc(data, (data_size + 1) * sizeof(*data)));
            if (!tmp) {
    ...
    

    Ich würde eher sagen: Man sollte in Schleifenbedingungen keinen , Operator verwenden.



  • @hustbaer Ja, Du hast natürlich völlig recht, ist eine saublöde Angewohnheit 😞



  • Naja, man kann da auch unterschiedlicher Meinung sein. Ich finde es halt doof.



  • @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Gibt es keine Faelle in denen es sinnvoll waere sowas selbst zu implementieren? Was ist denn sonst der Sinn von dem Ganzen, wenn man es nie benutzen soll? Also natuerlich spart man sich ne Menge Arbeit und Probleme wenn man es nie benutzt, aber trotzdem eigenartig

    Man darf nicht vergessen, dass das ganze historisch gewachsen ist. Es wurden kaum Bestandteile der Sprache entfernt, so dass fast jeder alter Code übersetzt werden kann. Bei einigen modernen Modesprachen ist das nicht der Fall, da wird nach Belieben die Kompatibilität mit alten Code gebrochen – nicht so bei C++.

    Vor der ersten ISO Norm von C++ war es faktisch unmöglich Programme zu schreiben, die kein new/delete verwendet haben. Mit der ersten ISO Norm (1998) wurden auch die Container zum Teil der Sprache, während sie vorher als STL durch die C++ Community verbreitet wurden. Die letzte von Stroustrup selbst publizierte Version (cfront 3.0) war sehr viel einfacher als das was es heute als ISO Norm gibt. Damals hatte die Sprachdefinition keine 300 Seiten (als Anhang in Die C++ Programmiersprache), mittlerweile ist die ISO Norm mehr als 1600 umfangreich.

    Es gab dann für C++98 ein sehr gutes Einsteigerlehrbuch für modernes C++: Accelerated C++ von Koenig und Moo. Leider haben die beiden Autoren nie eine aktuelle Auflage für C++11 oder neuer publiziert. Was man hier bei den Aufgaben mal wieder sieht: die Intention der Dozenten ist es, lowlevel Speicherverwaltung zu lehren, weil man das einfach mal gemacht haben muss. Aber das passt leider gar nicht mehr zu modernem C++ und würde dort nur in extremen Ausnahmen eingesetzt z.B. wenn man einfängt einen eigenen Container zu schreiben, der dann aber wiederum nicht direkt auf Speicheranforderungen setzen würde, sondern einen Allokator nutzen würde. An dieser Stelle hatte ich einen Allokator gepostet, der die Allokation auf einem NUMA-System simuliert. Man kann daran sehen, wie man im Prinzip einen Allokator zu schreiben hat. Und mein Allokator nutzt für die eigentliche Speicherverwaltung zwar malloc und free, legt aber den Zeiger in einem std::unique_ptr ab, und übergibt diesem bei Konstruktion gleich die free Funktion, damit der std::unique_ptr sie selbst bei Bedarf aufruft.

    Ich habe vor einiger Zeit damit angefangen eine eigene Matrixklasse zu schreiben, die per Allokator Speicher anfordert, aber da das ganze alles andere als trivial ist, ist sie noch nicht fertig und vor allem getestet. D.h. eigene Container zu schreiben ist alles andere als trivial.



  • @hustbaer Nana, passt scho, es IS unübersichtlich und z'sammg'staucht. Geht leserlicher. 👍



  • @marcel91200 auf @hustbaer s Einwand:

    #include <limits>   // std::numeric_limits<>
    #include <cstdlib>  // std::realloc(), std::free(), EXIT_FAILURE
    #include <memory>   // std::make_unique<>()
    #include <iostream> // std::cin, std::cout, std::cerr
    
    bool isPermutation(int *perm, int count)
    {
        auto cmp = std::make_unique<bool[]>(count);
    
        for (int i = 0; i < count; ++i)
            if (perm[i] < 0 || perm[i] >= count)
                return false;
            else cmp[perm[i]] = true;
    
        for (int i = 0; i < count; ++i)
            if (!cmp[i])
                return false;
    
        return true;
    }
    
    bool isSorted(double *data, int dataCount, int *perm)
    {
        if (!isPermutation(perm, dataCount))
            return false;
    
        for (int i = 0; i < dataCount - 1; ++i)
            if (data[perm[i]] > data[perm[i + 1]])
                return false;
    
        return true;
    }
    
    int main()
    {
        double *data = nullptr;
        int data_size = 0;  // nein, eigentlich std::size_t
    
        std::cout << "Your values:\n";
        for (double input; ; ++data_size) {  // 1)
            std::cout << data_size + 1 << ": ";
            if (!(std::cin >> input))
                break;
    
            double *tmp = static_cast<double*>(std::realloc(data, (data_size + 1) * sizeof(*data)));
            if (!tmp) {
                std::cerr << "Not enough memory :(\n\n";
                std::free(data);
                return EXIT_FAILURE;
            }
            data = tmp;
            data[data_size] = input;
        }
    
        if (!data_size) {
            std::cerr << "No input :(\n\n";
            return EXIT_FAILURE;
        }
    
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    
        std::cout << "\nYour indices:\n";
        auto indices = std::make_unique<int[]>(data_size);
        for (int i = 0; i < data_size; ++i) {
            std::cout << i + 1 << ": ";
            if (!(std::cin >> indices[i])) {
                std::cerr << "Input error :(\n\n";
                std::free(data);
                return EXIT_FAILURE;
            }
        }
    
        std::free(data);
    }
    

    Kommaoperator ist zwar nice (und important) to know aber man muss nicht übertreiben.

    1) Fehlt bei einer for-loop die Bedingung so wird `true` angenommen. Idiomatische Endlosschleife in C und C++: for (;;) lies: for-ever.


Log in to reply