Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » malloc, realloc Problem

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: [ 1 ] > 2 <
010
28.08.2003, 20:16 Uhr
virtual
Sexiest Bit alive
(Operator)


Falls Du realloc richtig anwendest, gibt es vielleicht noch den Punkt, daß es rein von der Performance her ziemlich ungünstig ist, das Array jedesmal nur um 1 zu vergößern. Übliche Alternativen sind entweder exponentielles Wachstum oder lineare in großen Schritten. So oder so kommt man dann ziemlich schnell zum Punkt, daß man nicht mehr einfach mit einem Pointer auf den Speicher arbeitet, sondern gleich mit einer Struktur. Nehmen wir an, du willst ein Array dynamisch wachsen/schrumpfen lassen, welches aus lauter Zeilen der Länge 256 Zeichen (inkl 0 Byte besteht:

C++:
typedef char zeile_t[256]; /* Typ für eine Zeile */


Dann würde man sich Anstelle eines simplen Arrays sowas vorstellen können:

C++:
typedef struct
{
    unsigned aktuell; /* Aktuelle anzahl der Zeilen */
    unsigned maximal; /* maximale Anzahl der Zeilen */
    zeile_t* zeilen; /* Das eigentliche Array */
} inhalt_t;


Man würde sich eine Routine schreiben, die in der lage ist, eine solche Struktur zu initialisieren:

C++:
void InitialisiereInhalt(inhalt_t* inhalt)
{
    memset(inhalt, 0, sizeof(*inhalt));
}


Natürlich eine, um eine solche Struktur wieder freizugeben:

C++:
void LoescheInhalt(inhalt_t* inhalt)
{
    unsigned i;
    for(i=0; i<inhalt->aktuell; ++i)
    {
         if (NULL != inhalt->zeilen[i]) free (inhalt->zeilen[i]);
    }
    if (NULL != inhalt->zeilen) free(inhalt->zeilen);
    memset(inhalt, 0, sizeof(*inhalt));
}


Tja, nun der interessante Teil: nämlich Zeilen anfügen. Deine Originalmethod sähe so aus:

C++:
int FuegeZeileHinzu(inhalt_t* inhalt, zeile_t zeile)
{
    if (inhalt->aktuell==inhalt->maximal)
    {
        zeile_t* temp = realloc(inhalt->zeilen, (inhalt->maximal+1)*sizeof(zeile_t*));
        if (NULL == temp)
        {
            /* fehler */
            return -1;
        }
        ++inhalt->maximal;
        inhalt->zeilen = temp;
    }
    inhalt->zeilen[inhalt->aktuell] = malloc(sizeof(zeile_t));
    if (NULL == inhalt->zeilen[inhalt->aktuell])
    {
         /* fehler */
         return -1;
    }
    memcpy(inhalt->zeilen[inhalt->aktuell++], zeile, sizeof(zeile));
    return 0;
}


In diesem Beispiel zahlt sich natürlich die Struktur nicht so dolle aus: maximal und aktuell haben immer den gleichen Wert, wenn nicht grade was schief gegangen ist. Aber Sobald man nicht immer um 1 vergrößert, sondern eben zB um 10, wird ein Schuh draus:

C++:
#define INCSIZE 10
int FuegeZeileHinzu(inhalt_t* inhalt, zeile_t zeile)
{
    if (inhalt->aktuell==inhalt->maximal)
    {
        zeile_t* temp = realloc(inhalt->zeilen, (inhalt->maximal+INCSIZE)*sizeof(zeile_t*));
        if (NULL == temp)
        {
            /* fehler */
            return -1;
        }
        inhalt->maximal += INCSIZE;
        inhalt->zeilen = temp;
    }
    inhalt->zeilen[inhalt->aktuell] = malloc(sizeof(zeile_t));
    if (NULL == inhalt->zeilen[inhalt->aktuell])
    {
         /* fehler */
         return -1;
    }
    memcpy(inhalt->zeilen[inhalt->aktuell++], zeile, sizeof(zeile));
    return 0;
}


Man sieht schnell ein, daß bei gleichem Programm ds realloc wesentlich weniger aufgerufen wird. Wenn Du Die Routinen zB einsetzt, um große Dateien einzulesen, ist der Effekt deutlich messbar. Noch extremer ist es natürlich, enn du jedesmal den Speicher verdoppelst:

C++:
int FuegeZeileHinzu(inhalt_t* inhalt, zeile_t zeile)
{
    if (inhalt->aktuell==inhalt->maximal)
    {
        zeile_t* temp;
        if (inhalt->zeilen)
            temp = realloc(inhalt->zeilen, (inhalt->maximal*2)*sizeof(zeile_t*));
        else
            temp = malloc(sizeof(zeile_t*));
        if (NULL == temp)
        {
            /* fehler */
            return -1;
        }
        if (inhalt->zeilen)
           inhalt->maximal *= 2;
        else
           inhalt->maximal = 1;
        inhalt->zeilen = temp;
    }
    inhalt->zeilen[inhalt->aktuell] = malloc(sizeof(zeile_t));
    if (NULL == inhalt->zeilen[inhalt->aktuell])
    {
         /* fehler */
         return -1;
    }
    memcpy(inhalt->zeilen[inhalt->aktuell++], zeile, sizeof(zeile));
    return 0;
}


Allerdings ist hier daß problem, daß uU ziemlich viel Speicher brach liegt. Man bedenke den Fall, daß man eine Zeile mit 65537 Zeilen Einliest: 4*65535 Bytes einfach für die Katz!

Vor allem macht das Beispiel aber deutlich, daß es sinnvoll ist, auch in C für scheinbar einfache Dinge schnell Strukturen und eindeutig zugeordnete Methoden, äh nat. Funktionen zu schreiben.

Beispielcode ungetestet.
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
011
28.08.2003, 20:36 Uhr
Pablo
Supertux
(Operator)


Wie vergrößert realloc eigenrlich den Speicher? Macht er eine Kopie von alles?

Sagen wir Mal, ich hätte:

C++:
datentyp_xyz* x;
...
x = (datentyp_xyz*) malloc(sizeof(datentyp_xyz)*10);
...
x = realloc(x, sizeof(datentyp_xyz)*11);



Wie löst realloc das Problem? Macht eine neue Kopie mit den ersten 10 Elementen und "fügt eins hinzu" oder "fügt" einfach an x hinzu. Anders ausgegruckt: O(n) oder O(1).

Weil wenn O(n), dann war meine Implementation nicht optimal.
--
A! Elbereth Gilthoniel!
silivren penna míriel
o menel aglar elenath,
Gilthoniel, A! Elbereth!
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
012
28.08.2003, 21:37 Uhr
~Martin
Gast


Soweit ich weiß versucht realloc erstmal einfach Speicher dranzuhängen, wenn direkt dahinter noch freier Speicherplatz ist. Ist das aber nicht möglich so sucht realloc eine neue passende Speicherstelle, kopiert sämtliche Daten dort hinein, gibt den alten Speicher frei und die Adresse des neuen Speichers zurück.
Wenn man nun immer um ein Element erhöht ist das nicht so effektiv, da realloc ja eine möglichst passende Stelle sucht.
Wie Tommix ja schon schrieb muß deswegen der Rückgabewert von realloc genutzt werden, denn die neue Speicheradresse kann, muß aber nicht dieselbe sein (kann man ganz gut sehen, wenn man sich jedesmal den Wert der Speicheradresse mit ausgeben läßt).
Pech ist nur, wenn realloc nicht mehr genügend freien Speicher findet, denn dann dürfte der Inhalt des alten Speichers auch verloren sein, obwohl man u.U. noch mit einem "wilden" Zeiger darauf zeigen kann. Aber wenn einem der Speicher ausgeht hat man sowieso ein Problem...

Gruß,
Martin
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: [ 1 ] > 2 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: