realloc() mit struct und Array?



  • code_pilot schrieb:

    Öhhh und wie wende ich new an? Kannst du mir mal'ne Beispielzeile adden?

    TA * test = new TA[2];
    // mit test arbeiten ... dann freigeben:
    delete[] test;
    

    Das sorgt dafür, dass die Konstruktoren der Members von TA aufgerufen werden. Dann funktioniert das ganze auch, wenn deiner Version mal das Glück ausgeht 😉

    Vorkriegscompiler? Tut mir leid, aber ich habe nicht das Geld, mit jeden Monat eine neue C++Builder-Version zuzulegen.

    Sorry, sollte keine Beleidigung sein. Ich hab selbst das Vergnügen, dass mein aktuelles Projekt auch auf dem Borland C++ 5.2 compilieren muß, das ist nicht immer wirklich angenehm, weil sich seitdem entscheidende Details geändert haben.

    Die strings die ich benutze gibt es auch unter Linux und ich finde den Datentyp ganz gut, da man hier keine 500 kB VCL-Libs braucht.

    Die string-Klasse gibt es sogar im C++ Standard 😉 Wenn du <fstream.h> einbindest, ist es wohl höchstens ein angenehmer Nebeneffekt, dass du die string-Klasse dazubekommst. Normalerweise ist die im Header <string>. Mal ein Beispiel:

    #include <string>
    #include <iostream>
    // falls der Compiler die letzte Zeile nicht schluckt: #include <iostream.h>
    // meiner alter 5er Borland kennt die neue iostream noch nicht, deiner vielleicht schon ...
    using namespace std;
    
    int main() {
      string test = "hallo";
      cout << test << endl;
    }
    

    Dieses Beispiel (mit iostream.h) funktioniert auf meinem 5er Compiler, der ist von 1994. Also sollte es bei dir auch gehen, wie alt ist der Builder 4 denn, bzw. welche Compilerversion liegt darunter?



  • @Bashar:

    Ok, es funktioniert jetzt so einigermassen. Jetzt habe ich aber weiterhin noch das Problem, das ich das dingen nicht erweitern kann. Folgende main()-Funktion schlug fehl:

    TA *test;
            memset(&test, 0, sizeof(TA));
    
            test = new TA[2];
    
            if(test == NULL)
            {
                    printf("da war wohl ein gferl\n");
                    getch();
                    exit(1);
            }
    
            test[0].vName = (string) "vName1 von 5 oder so";
            test[0].vValue = (string) "und ein Testwert, die erste";
            test[1].vName = (string) "vName2 von 5 oder so345345";
            test[1].vValue = (string) "226 test";
    
            TA* tmp = test + 2* sizeof (TA);
            tmp = new TA;
    
            test[2].vName = (string) "vName3 von 5 oder soghjkhgk";
            test[2].vValue = (string) "unddfgdfhgfh ein Tese";
    
            for(int i=0;i<3;i++)
            {
                    printf("%s  --->>>> %s\n", test[i].vValue.c_str(), test[i].vName.c_str());
            }
    
            getch();
    
            printf("error...");
            return 0; //<- hier schmiert er ab, wenn ganz oben new[3] steht ... warum auch immer  :confused:
    

    Kannst du mir da auch noch helfen??

    Grüsse, ~cp



  • memset(&test, 0, sizeof(TA));
    

    ??? Undefiniertes Verhalten würd ich sagen

    test = new TA[2];
    

    Du greifst auf 3 Elemente zu, aber hast nur 2 alloziert => U.V.

    test[0].vName = (string) "vName1 von 5 oder so";
    

    Der Cast ist unnütz.

    TA* tmp = test + 2* sizeof (TA); 
            tmp = new TA;
    

    Du setzt tmp erst auf die Position genau hinter dem allozierten Bereich für test, dann weist du tmp einen neu allozierten Block zu. Wo ist der Sinn? Soll das das realloc ersetzen?

    Es gibt kein realloc für new. Dafür gibt es die Containerklasse vector:

    #include <vector>
    #include <string>
    using namespace std;
    struct TA {
      string vName, vValue;
    };
    int main() {
      vector<TA> test(2); // 2 TA-instanzen werden default-konstruiert
      test[0].vName = "vName 1 von 5 oder so";
      // ...
      test.resize(3);
      vName[2].vName = // ...
      // keine delete, kein nichts, der Destruktor von vector kümmert sich um alles
    }
    


  • memset(&test, 0, sizeof(TA));
    

    ??? Undefiniertes Verhalten würd ich sagen

    Wieso? Ich sehe bei sowas oft den Fehler nicht. für mich sieht das einfach nach ner umständlichen Schreibweise von test=0 aus. Wieso einfach, wenns auch kompliziert geht.

    include <iostream> 
    #include <string>
    #include <vector>
    
    using namespace std;
    
    struct TA 
    { 
            string vName; 
            string vValue; 
    }; 
    
    //--------------------------------------------------------------------------- 
    int main(int argc, char* argv[]) 
    { 
            vector<TA> test(2);
    
            test[0].vName = "vName1 von 5 oder so"; 
            test[0].vValue = "und ein Testwert, die erste"; 
            test[1].vName = "vName2 von 5 oder so345345"; 
            test[1].vValue = "226 test"; 
    
            test.push_back(TA());
            // alternativ test.resize(3); 
    
            test[2].vName = "vName3 von 5 oder soghjkhgk"; 
            test[2].vValue = "unddfgdfhgfh ein Tese";
    
            for(int i=0;i<3;++i) 
            { 
                    cout << test[i].vValue << "  --->>>>" << test[i].vName << '\n'; 
            } 
            cin.get(); 
            return 0; 
    }
    


  • Helium schrieb:

    memset(&test, 0, sizeof(TA));
    

    Wieso? Ich sehe bei sowas oft den Fehler nicht. für mich sieht das einfach nach ner umständlichen Schreibweise von test=0 aus. Wieso einfach, wenns auch kompliziert geht.

    test ist ein Pointer auf TA. Größe vermutlich 4. TA enthält 2 string-Instanzen, sizeof(TA) daher beispielsweise 16. Klingelts?

    (Davon abgesehen ist die Zuweisung an der Stelle sowieso unnötig)



  • Danke. Sowas übersehe ich immer.



  • Hi!
    Ja danke erstmal, das letzte Beispiel mit den Vektoren Funktioniert ja einwandfrei *freu*. Gibt es durch diese Sektoren denn keine Einschränkungen, oder könnt ihr mir mehr Informationen geben, denn von sowas habe ich bisher nur am Rande mal was gehört.

    Anders wäre das ganze ggf. nicht lösbar, oder?

    Gruss & Dank,
    code_pilot



  • http://www.c-plusplus.net/stl.htm

    Anders wäre das ganze ggf. nicht lösbar, oder?

    Wie gesagt, es gibt kein renew analog realloc. Wenn man also ein mit new alloziertes Array vegrößern will, bleibt einem nichts anderes übrig, als einen neuen, größeren Speicherblock zu allozieren, alles umzukopieren, und den alten Block zu löschen. vector macht im Prinzip hinter den Kulissen genau das. Dazu kommt, dass vector immer noch ein wenig uninitialisierten Reserve-Speicher hat, so dass er nicht bei jedem push_back neuen Speicher besorgen muß.



  • Ich habe Dir schon gesagt, wie es funktioniert, ohne Vektoren zu verwenden. Das was ich gemacht habe, und wovor du so eine Angst hast, macht die STL intern. Wahrscheinlich nicht genauso, da der Code sehr viel optimiert wurde und ich wohl nicht in der Lage bin die STL nachzustellen, aber im großen und ganzen sollte es das selbe sein (die Klasse vector ist ein Teil der STL der das Verwalten von Arrays und Listen vereinfacht).

    Nochmal, um es in diesem Forum zur Diskussion zu stellen (damit ich eventuell auch über meine Fehler aufgeklärt werde):

    Um einen Array von Elementen zu allokieren, machst du dir entweder einen Array oder einen Zeiger. Ob du einen Zeiger oder einen Array nimmst ist egal, da der Array beim kompilieren intern zu einem Zeiger umgewandelt wird.

    Die Anzahl der Elemente, die Du am Anfang brauchst, allokierst du ganz normal mit new oder malloc. Traditionell verwende ich hier einen Array auf Integerwerten (ich verwende ihn als Zeiger), für deine Strings ersetzt du einfach den Datentyp:

    #define n 10
    int* ptr_int = NULL
    ptr_int = new int [n];
    /*oder ptr_int = (int*) malloc (10* sizeof (int));*/

    aber das ist ja nicht die schwierigkeit. Die Schwierigkeit die Du hast, ist nun weitere Elemente zu allokieren. Hier hast du unterschiedliche Möglichkeiten:

    a.) du kannst new erneut für die weiteren Elemente aufrufen, und einen zeiger auf den neuen Speicherbereich anlegen. Dann musst du darauf achten dass du dein Programm darauf hinweisst, nach entsprechend vielen Elementen (hier 10) an die neue adresse zu springen und dort weiter zu machen. Dies könntest du z.B. realisieren, indem du jeweils jedem Zeiger einen Zeiger hinzufügst, also dein Array zweidimensional machst und jeweils die Adressen als zeiger auf einen zeiger speicherst

    b.) du kannst wie schon angesprochen die Daten serialisieren, d.h. du kopierst den ersten Array in einem buffer und löschst gibst den reservierten Bereich wieder frei. Dadurch hst du aber einen extrem großen Speicheraufwand, weil pro Vergrößerung des Arrays der ganze Buffer kopiert und gelöscht werden muss.

    c.) du nutzt die Überladene Version von new. new ist ein Operator der Standartmäßig schon überladen ist. Es gibt deshalb mehrere Versionen des new Operators:

    1. new ()
    2. new
    3. new []
    4. new () []
    .......

    Es gibt daher auch einen new Operator, der Speicher ab einer Adresse reserviert, die Du angibst. Dir sollte es eigendlich möglich sein, diese Adresse zu errechnen und diese new zu übergeben:

    void* newadress = oldadress + sizeof (DATENTYP;
    newadress = (void*) new (newadress) [BYTES];



  • Parapiler schrieb:

    Nochmal, um es in diesem Forum zur Diskussion zu stellen (damit ich eventuell auch über meine Fehler aufgeklärt werde):

    OK, angenommen 🙂

    Ob du einen Zeiger oder einen Array nimmst ist egal, da der Array beim kompilieren intern zu einem Zeiger umgewandelt wird.

    Das ist falsch. Das Speicherlayout ist völlig verschieden: ein Array besteht aus hintereinanderliegenden Elementen, ein Zeiger ist eine Adresse. Ein Array wird aber fast überall automatisch in einen konstanten Zeiger auf sein erstes Element umgewandelt ("fast" weil es bei sizeof nicht passiert).

    Es gibt daher auch einen new Operator, der Speicher ab einer Adresse reserviert, die Du angibst. Dir sollte es eigendlich möglich sein, diese Adresse zu errechnen und diese new zu übergeben:

    void* newadress = oldadress + sizeof (DATENTYP;
    newadress = (void*) new (newadress) [BYTES];

    Der Placement-new Operator reserviert keinen Speicher, sondern geht davon aus, dass du einen Pointer auf einen von dir bereits reservierten Speicher übergibst. Dann konstruiert er dort drin entsprechend Objekte. Bei primitiven Typen wie int tut Placement-new daher überhaupt nichts.

    Wenn du das so machst wie du grad vorgeschlagen hast, ist das wie eine ganz normale Arrayüberschreitung, dh du zerstörst wahrscheinlich andere Daten, die dorthinter liegen.


Anmelden zum Antworten