realloc : Verständnis / Rechenproblem



  • Hallo Leute,

    ich habe bis jetzt nur malloc benutzt um Zeigern die ich für Strings brauche speicher zu zuweisen.

    Jetzt bin ich durch ein kleines Problem auf realloc gekommen, habe aber erstmal ein Verständnisproblem.

    char *cp;
    int *ip;
    
    cp=malloc(sizeof(char));  // cp hat jetzt "Speicherplatz" für 1 Char, der "Wert ist Müll
    ip=malloc(sizeof(int));  //  ip hat jetzt  "Speicherplatz" für 1 Int, Wert, Müll
    
    *cp='x';
    *ip=2;
    
    cp=realloc(cp, sizeof(char)*2);  // Was passiert mit dem 'x' ? Realloc erweitert, also müsste das 'x' erhalten bleiben...
    //Wie sieht der Speicherplatz jetzt aus? Wird der "angehängt"? Hat cp dann jetzt sizeof(char)*3 ??
    //Falls ja, ok, kann ich nen String reinmachen, ok , aber bei : 
    
    ip=realloc(ip, sizeof(int)*2)); // Speicherplatez?? Ein Zeiger ist ja kein Array, kann ich da jetzt "mehrere" Werte speichern?  Nö oder? realloc auf diese Weise fürn Ar...  ?
    

    Zu Grunde lag das Problem, das ich ich aus einer Datei Zeiche eingelesen habe und die in ein CHAR ARRAY gespeichert habe. Dies war 2000 Plätze groß.
    Wie´s der Teufel haben will, kamen jetzt 2212 Zeichen daher. Toll.

    Da war mein Gedanke ich will ein Array, das bei jedem Schleifendurchlauf das Array "vergrößere", aber so geht das nicht wirklich oder?

    Pseudocode:

    while (j!=EOF){
        j=fgetc(dateizeiger);
        // Meinspeicherbereich = j  Mit Array war  *ap=j, ap++   (ap zeigte aufs Array)
        // Meinspeicherbereich++
    }
    

    Ich hoffe Ihr wisst worauf ich hinaus will, kanns momentan nicht besser erklären.


  • Mod

    Grundsätzliches zu realloc:
    Es vergrößert den gegebenen Bereich. Wenn es dafür einen neuen Speicherbereich braucht, dann bekommt man einen anderen Zeiger neu gemalloct und realloc kopiert automatisch die alten Daten in den neuen Bereich und gibt den alten Bereich frei. Also im Grunde ein rundum sorglos Paket.
    (Bis auf eine Kleinigkeit: Wenn die Möglichkeit besteht, dass einem der Speicher ausgeht. realloc gibt dann nämlich, wie malloc, Null zurück. Aber es gibt den alten Speicher nicht frei (der soll dann ja auch sicher erhalten bleiben). Das heißt, wenn man einfach direkt den alten Zeiger mit der Null überschreibt, dann kommt man an den alten Speicher nicht mehr ran! Die super-korrekte Benutzung von realloc wäre daher, dass man den Rückgabewert von realloc erst einmal temporär speichert, mit Null vergleicht und nur im Erfolgsfall den alten Zeiger überschreibt.)

    Zum Thema dynamische Zeichenketten in C:
    Doch, realloc ist da schon richtig. Aber bei deinen Kommentaren verstehe ich nicht, wo das realloc sein soll.

    Nur um ganz sicher zu sein: Dir ist schon klar, dass realloc nur auf Zeigern funktioniert, die man vorher von malloc/calloc/realloc bekommen hat? Das geht nicht mit statischen Arrays!



  • Der "Pesuedocode" beinhaltet noch kein realloc oder malloc. Der arbeitete noch mit Statischem Array.

    Die reallocs, bzw. die Kommentare dazu im obrigen Code... Stimmt das dann so?

    Also konkret:
    zeiger=malloc(sizeof(int))
    .....

    zeiger=realloc(zeiger, WERT) => ist Zeiger dann ein Dynamisches INT Array? Hä? Nie und nimmer.

    Der Pseudocode war ohne Speichererweiterung, ich dachte wenn das funktioniert, dann soll mit jedem Schleifendurchlauf der Speicherbereich um 1 mal sizeof(int) oder sizeof(char) erweitert werden. Somit hätte ich (fast) immer Genug Platz, egal wieviele Zahlen oder bei Char eben Zeichen kommen. ..



  • Hallo,

    wäre es nicht viel besser, wenn du vor dem Einlesen die maximale Größe der Datei (bzw. des auszulesenden Bereichs) bestimmst und dann einfach nur einmalig malloc aufrufst?
    Auf keinen Fall solltest du bei jedem Einlesen eines Zeichens jedesmal realloc() aufrufen, außer du willst unperformanten Anti-Code schreiben.

    Alternative wäre das sukzessive Vergrößern des Speichers in Blöcken, z.B. wie der C++ std::vector, welcher bei den meisten Implementierungen 2*X+C Speicher reserviert, also einer Verdoppelung des Speichers, sobald der Speicher voll ist.



  • Hallo,

    wäre es nicht viel besser, wenn du vor dem Einlesen die maximale Größe der Datei (bzw. des auszulesenden Bereichs) bestimmst

    DAS... wäre die "supreme" Lösung, nur leider hab ich nicht den blassesten Schimmer wie ich das machen soll. 😕 😕

    Prinzipell würde das mit dem realloc aber gehen oder? Also mit jedem durchlauf erhöhen?

    Das ganze ist eher theoretischer bzw. experimenteller Natur.


  • Mod

    beginner88888 schrieb:

    Hallo,

    wäre es nicht viel besser, wenn du vor dem Einlesen die maximale Größe der Datei (bzw. des auszulesenden Bereichs) bestimmst

    DAS... wäre die "supreme" Lösung, nur leider hab ich nicht den blassesten Schimmer wie ich das machen soll. 😕 😕

    Entweder mit plattformspezifischer Funktion stat (ist aber auf so gut wie allen Plattformen vorhanden); oder ans Ende springen, Position bestimmen, wieder an den Anfang springen. Guck mal in eine Referenz des stdio.h-Headers, da findest du entsprechende Funktionen.

    Prinzipell würde das mit dem realloc aber gehen oder? Also mit jedem durchlauf erhöhen?

    Ja.



  • Entweder mit plattformspezifischer Funktion stat (ist aber auf so gut wie allen Plattformen vorhanden); oder ans Ende springen, Position bestimmen, wieder an den Anfang springen. Guck mal in eine Referenz des stdio.h-Headers, da findest du entsprechende Funktionen.

    Werd ich machen, da dies wie schon geschrieben bestimmt die feinste ARt ist.

    Aber es Bleibt:

    Zitat:

    Prinzipell würde das mit dem realloc aber gehen oder? Also mit jedem durchlauf erhöhen?
    Ja.

    Wäre das dann korrekt:?

    int *iptr;
    
    iptr=malloc(sizeof(int)*2);   //Könnte jetzt 2 Werte Speichern??
    
    *iptr=5;
    iptr++;      // Wenn ich jetzt den Zeiger erhöhe, befindet er sich noch in dem zugewiesenen Speicher??
    *iptr=10;    // Das kann doch nicht gehen so oder? Wenn ich den Zeiger erhöhe, passt das dann schon noch??
    iptr=realloc(iptr, sizeof(int));  // Jezt + 1 INT ??
    

  • Mod

    Das ist völliger Quark. Guck dir mal ein paar Referenzen oder Beispiele zu realloc an.



  • Du hättest auch mal in die FAQ sehen sollen:
    Dynamischer Speicher mit malloc/calloc/realloc/free

    Und hier auch der passende Beitrag für die Größe einer Datei:
    Anzahl der Zeichen in einer Textdatei

    Oder eben alternativ mittels der von SeppJ erwähnten Funktion stat, s. z.B. Zugriff auf das Dateisystem (P.S: Die Überschrift darin, ist aber mal wieder goldig 😉



  • Ja, werd ich machen.
    Das Zeigererhöhen ist schon falsch oder? Da geh ich aus dem reservierten Speicher raus??



  • Aus den Links:

    // für ptr wurde bereits Speicher reserviert
    ptr = realloc ( ptr, 10 * sizeof(*ptr) );
    

    soweit bin ich dann aber mit dem realloc nicht weg, sizeof(*ptr) ist bei mir sizeof(int).
    Die Überprüfung von realloc mit dem temp jetzt mal aussen vor.


  • Mod

    Das was du da vorher mit dem Pointer anstellst ist aber total verkehrt. Ich bin mir nicht sicher, dass du verstanden hast, was realloc macht. realloc fügt keinen Speicher an irgendeiner Stelle ein. realloc nimmt einen mit malloc reservierten Speicherbereich und gibt einen zurück, der eine andere Größe hat.



  • Heut steh ich wohl nicht nur auf dem Schlauch....

    das was du da vorher mit dem Pointer anstellst ist aber total verkehrt

    Platz für 2 Werte, 2 Werte schreiben? Ich checks heut echt nciht.

    realloc nimmt einen mit malloc reservierten Speicherbereich und gibt einen zurück, der eine andere Größe hat.

    ...inklusive dessen was schon im speicher steht. So hab ichs gelesen.

    Und aus dieser Quelle:
    http://pronix.linuxdelta.de/C/standard_C/c_programmierung_17.shtml

    Habe ich schon den Eindruck geahabt,
    das :

    int *ptr;
    ptr=malloc(sizeof(int)*2)
    

    ...Speicherplatz für 2 INT Werte reserviert, die ich wie ein Array behandeln kann.

    *ptr=5  // 1. Speicherplatz
    *(ptr+1)=10 //  wäre ptr++ und dann *ptr=10 nicht dasgleiche?  jedenfalls 2. Speicherplatz.
    

    Ich habe es so verstanden, das ich nun mit
    ptr=realloc(ptr, WERT)
    den bereits Vorhandenen Speicherplatz um X Elemente vergrößere wenn WERT=sizeof(int)*X und die bisherigen "Einträge" nicht verliere.

    von "http://pronix.linuxdelta.de/C/standard_C/c_programmierung_17_1.shtml" :

    Bei einer Vergrößerung des Speicherplatzes mit realloc() behält der vordere Teil auf jeden Fall seinen Wert, und der neue Teil wird einfach hinten angehängt. Dieser angehängte Wert ist aber wie bei malloc() undefiniert.


  • Mod

    Das Missverständnis scheint wohl zu sein, dass ptr++ etwas sehr anderes ist als ptr + 1 .



  • Der Vorhang lüftet sich.

    Ich benutze ptr++ normal so

    int arr[10], *iptr;
    
    iptr=arr;
    
    *iptr=5;
    iptr++; 
    *iptr=10; 
    usw usw
    

    In einer Schleife hald.

    Bei meinem vorherigen Codes, würde das ptr++ den Zeiger erhöhen, da es aber kein richtiges Array ist, zeigt er hald irgendwohin, *(ptr+X)=Y weist aber dem Xten Element des "Arrays" das mit Malloc oder Realloc erzeugt wurde den Wert Y zu.
    Richtig soweit?

    Aber würde dann in einer schleife der Code:

    *(zeiger+X)=Wert;   // Zeiger vorher z.B.. zeiger=malloc(sizeof(int)*2);
    zeiger=realloc(zeiger, sizeof(int));  // Verkleinert den Speicher?
    

    .. bei jedem Durchlauf das "Array" um einen Speicherplatz erhöhen, oder müsste es:

    zeiger=realloc(zeiger, aktuelle_Größe + sizeof(int));   //ist für aktuelle_Größe sizeof(zeiger) möglich? nicht oder? liefert nur die Größe des Zeigers .
    

    sein? aktuelle_Größe könnte ich ja mit Hilfe einer Laufvariablen in der Schleife ermitteln...?



  • Du darfst aber nur den Zeigerwert an realloc() geben, den du mit malloc() (oder einem vorherigen realloc())-Aufruf erhalten hast, d.h. du darfst den nicht einfach ändern.

    Du müßtest dir den Originalzeiger in einer anderen Variable speichern, z.B.

    int *iptr, *tmp;
    
    iptr=malloc(sizeof(int)*2);   //Könnte jetzt 2 Werte Speichern??
    
    tmp = iptr;
    
    *iptr=5;
    iptr++;
    *iptr=10;
    
    iptr=realloc(tmp, sizeof(int)*3);
    

    Sonst kann die realloc()-Methode nichts mit dem Zeigerwert anfangen (da sie intern eine Liste aller Speicherblöcke verwaltet und den übergebenen Wert überprüft, ob sie ihn kennt - denn sie muß nach dem Kopieren ja den alten Speicher mit freee() wieder freigeben).

    Besser wäre im obigen Code naürlich, daß du den Originalzeiger behältst und für die Änderung dem temporären Zeiger verwendest, also

    int *iptr, *tmp;
    
    iptr=malloc(sizeof(int)*2);
    
    tmp = iptr;
    
    *tmp=5;
    tmp++;
    *tmp=10;
    
    iptr=realloc(iptr, sizeof(int)*3);
    

    So ich hoffe, jetzt ist dir ein bißchen mehr klar geworden über die Speicherverwaltung und den Umgang mit Zeigern.



  • So ich hoffe, jetzt ist dir ein bißchen mehr klar geworden über die Speicherverwaltung und den Umgang mit Zeigern.

    Nö, ich dachte ich habe mir gerade hergeleitet, warum

    *ptr=5;
    ptr++;
    *ptr=10;

    falsch ist. Was nun? ptr++ bringt mich doch aus dem Bereich den ich "reserviert" habe raus oder etwa nicht?

    Ich würde es jetzt so machen, alles zusammengefasst was ich nun zu verstehen glaube:

    int *iptr; *tmp, i=0;
    
    iptr=malloc(sizeof(int));
    tmp=iptr;
    
    while (irgend_eine_abbruchbedingung) { 
                     *(iptr+i)=Value;    // Davon ausgegangen das Value passend reinkommt          
                     i++;
                     iptr=realloc(tmp, sizeof(int)*i);
                     //Ohne Überprüfung jetzt ob realloc oder malloc nicht in die Hose gingen 
            }
    

  • Mod

    Ist ok, aber wenn du sowieso nicht prüfst, kannst du dir den Umweg über tmp auch sparen.

    Dir ist bekannt, dass *(foo + X) das gleiche ist wie foo[X] ?



  • Ja, das ist mir bekannt. Ich mach´s mal so und mal so. Ist eher geschmackssache oder??
    Das mit tmp dacht ich mir fast, trotzdem Danke @Th69

    Bitte noch eine Antwort:
    Th69 hat in meinem / seinen Code ja ptr++ benutzt.

    Stimmt meiner Herleitung, warum ptr++ falsch ist?

    Ich benutze ptr++ normal so

    int arr[10], *iptr;
     
    iptr=arr;
     
    *iptr=5;
    iptr++;  // Zeiger auf das nächste Element des "richtigen" Arrays
    *iptr=10;
    usw usw 	
    
    //In einer Schleife hald.
    

    ABER : Bei meinem vorherigen Codes

    int *iptr;
    
    iptr=malloc(sizeof(int)*2)); // Speicher für 2 INT
    
    *iptr=5;
    iptr++;
    *iptr=10;  // würde irgendwohin schreiben??
    

    , würde das ptr++ den Zeiger zwar auch erhöhen , da es aber kein "richtiges" Array ist( Er zeigt nur auf einen mit malloc vergrößterten Speicherbreich), würde er hald irgendwohin zeigen, aber nicht mehr im "Bereich" liegen?!?, *(ptr+X)=Y weist aber dem Xten Element des "Arrays" das mit Malloc oder Realloc erzeugt wurde den Wert Y zu.
    Richtig soweit?



  • Nein, du darfst ruhig ptr++ machen.
    Damit gehst du dann zum nächsten int, genauso wie beim regulären Array halt auch.
    Das Problem ist lediglich, dass realloc die von malloc erhaltene Adresse will, nicht eine, die ein paar Bytes weiter ist.


Anmelden zum Antworten