Forum: Compiler & IDEs Frage zu realloc()


von Reallokierer (Gast)


Lesenswert?

Wie funktioniert eigentlich realloc()?
Sucht das Betriebssystem(?) quasi bei einer Anfrage nach (zB.) 
Vergrößerung des Speichers einen neuen zusammenhängenden 
Speicherbereich? D.h, ich bekäme nach jedem:
addr = realloc(str, strlen(buffer)+new_len+1);
eine neue Adresse?
Und falls ja, was passiert dann mit dem alten Speicher? Dieser müsste ja 
dann automatisch freigegeben werden, oder?

von rmu (Gast)


Lesenswert?

Falls realloc != 0/NULL returniert, dann kann es ein Pointer zu einem 
anderen Speicherbereich sein (in dem Fall wird der alte Pointer mit free 
freigegeben), oder aber der selbe, kommt drauf an wies gemacht ist.

von Ralf D. (rad)


Lesenswert?

Reallokierer schrieb:
> Wie funktioniert eigentlich realloc()?
> Sucht das Betriebssystem(?) quasi bei einer Anfrage nach (zB.)
> Vergrößerung des Speichers einen neuen zusammenhängenden
> Speicherbereich? D.h, ich bekäme nach jedem:
> addr = realloc(str, strlen(buffer)+new_len+1);
> eine neue Adresse?
Ja, darauf das sich addr bei jedem realloc() ändert muss Deine Software 
vorbereitet sein. Aber immer passiert das nicht.

Reallokierer schrieb:
> Und falls ja, was passiert dann mit dem alten Speicher? Dieser müsste ja
> dann automatisch freigegeben werden, oder?

Der wird von der Speicherverwaltung recycelt und steht dann wieder als 
freier Speicher für alloc() zur Verfügung.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Reallokierer schrieb:
> Sucht das Betriebssystem(?)

Nicht das Betriebssystem, sondern die Heapverwaltung der 
Laufzeitumgebung Deines Compilers.

von holger (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Nicht das Betriebssystem, sondern die Heapverwaltung der
> Laufzeitumgebung Deines Compilers.

... die dann für eine kräftige Fragmentierung sorgen können.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

holger schrieb:
> ... die dann für eine kräftige Fragmentierung sorgen können.

Nach der gar keiner gefragt hatte.  Ich kann diese ewige „malloc ist
per se böse“-Leier einfach nicht mehr erhören.

Zum TE: der Variantenreichtum der Implementierungen ist dabei extrem
groß.  Das ist auch eine Frage, ob du auf einem System mit virtuellem
Speicher arbeitest (bei dem man recht großzügig sein kann) oder es
eine eher kleine Umgebung mit chronischer Speicherknappheit ist.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

holger schrieb:
> ... die dann für eine kräftige Fragmentierung sorgen können.

Dafür braucht es kein realloc, das geht mit malloc, calloc und 
free genausogut.

von Reallokierer (Gast)


Lesenswert?

Autor: Rufus Τ. Firefly (rufus) (Moderator) schrieb:
>Nicht das Betriebssystem, sondern die Heapverwaltung der
>Laufzeitumgebung Deines Compilers.
Dasch a ja interessant. Also kwasi so ne Art "Middleware" zwischen OS 
und Anwendung.


73

von holger (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> ... das geht mit malloc, calloc und free genausogut.

auf diesem Weg macht es realloc

@Jörg Wunsch
deswegen:
> ... die dann für eine kräftige Fragmentierung sorgen können.

achte auf das "sorgen können". Eine
> „malloc ist per se böse“-Leier
kann Ich in der Zeile nicht erkennen. Und ja, der TE hatte nicht danach 
gefragt. Etwas Hintergrund kann aber nicht schaden.

von Udo S. (urschmitt)


Lesenswert?

holger schrieb:
> Etwas Hintergrund kann aber nicht schaden.

Dann präzisiere das doch. Denn auf einem halbwegs modernen PC wirst du 
schon ziemlich viel tun müssen bis du da etwas merkst.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

holger schrieb:
> auf diesem Weg macht es realloc

Aha, du weißt es ja genau.

Hast du denn selbst schon mal eins geschrieben oder dir angesehen,
wie eins funktioniert, welches andere geschrieben haben?

Man kann es sicher auf diesem Wege machen, aber das ist eher ein
Notnagel denn eine vernünftige Implementierung.

Reallokierer schrieb:
> Also quasi so ne Art "Middleware" zwischen OS und Anwendung.

Ja, nennt sich Standardbibliothek.  OS muss übrigens nicht sein.

von Reallokierer (Gast)


Lesenswert?

Autor: Jörg Wunsch (dl8dtl) (Moderator)
> OS muss übrigens nicht sein.
Stimmt, jetzt wo du's wieder sagst... Für den Arduino Due mit SAM3X8E 
mit 64 + 32 Kbytes RAM steht der Befehl zur verfügung.

Was meinen die Experten? den Befehl verwenden? Oder eher Finger 
weglassen?



73

von Bitflüsterer (Gast)


Lesenswert?

Reallokierer schrieb:
> Autor: Jörg Wunsch (dl8dtl) (Moderator)
>> OS muss übrigens nicht sein.
> Stimmt, jetzt wo du's wieder sagst... Für den Arduino Due mit SAM3X8E
> mit 64 + 32 Kbytes RAM steht der Befehl zur verfügung.
>
> Was meinen die Experten? den Befehl verwenden? Oder eher Finger
> weglassen?

Das kann man pauschal nicht sagen. Da Du aber auf eine Prozessor mit 
64kB + 32kB RAM verweist (der möglicherweise nicht insgesamt von malloc, 
realloc verwaltetet wird), kann man hinzufügen, da0 die allgmeine 
Tendenz eher dahin geht, malloc/realloc bei sowenig (soviel) RAM zu 
vermeiden.

Es kommt auch auf den Verwendungszweck an. Wenn ein Programm relativ 
viele kleine Blöcke alloziiert ist das auf einem PC mit Gigabytes eher 
akzeptabel, als auf einem uC mit einigen Kilobyte. Du musst bedenken, 
dass die Speicherverwaltung selbst Speicher benötigt. Einige (soweit mir 
bekannt die allermeisten) Speicher-Verwaltungen benötigen einige Bytes 
pro angefordertem Block (genauer: zusammenhängendem Speicherbereich). 
Bei wenig Speicher und Anforderungen von Bereichen die nicht wesentlich 
über dem Overhead liegen, wird das schnell ineffizient.

von Klaus W. (mfgkw)


Lesenswert?

kommt drauf an...

Man muß sich halt bewusst sein, welche Resourcen man hat, wieviel man 
braucht und was man sinnvollerweise macht, wenn es eng wird.

Wenn man den Überblick nicht hat (warum auch immer), dann besser die 
Finger davon lassen.

Das ist wie mit gutem (scharfem) Werkzeug. Wenn man nicht damit umgehen 
dann, dann lieber nicht anfassen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Reallokierer schrieb:
> den Befehl verwenden?

Wenn du ein dynamisches Problem hast, wirst du mit einer statisch
vorallozierten Implementierung kaum besser kommen als mit malloc(),
denn du riskierst, dass deine Implementierung Bugs mit sich bringt,
die aus dem „offiziellen“ malloc() alle schon rausgeschüttelt sind.

Du solltest dir natürlich genau ansehen, auf welche Weise man das
mit der Newlib daherkommende (und durch deine selbst zu erstellende
_sbrk()-Backend-Funktion zu untersetzende) malloc() „auf die sichere
Seite“ bekommt, sprich, das Risiko einer Kollision von Heap und Stack
möglichst gering zu bekommen.

Eine Anwendung mit dynamischem Datenanfall muss immer für die
Situation gerüstet sein, dass sie mehr Daten bekommen könnte, als
sie Speicher zur Verfügung hat, und muss daraus einen Ausweg haben.
Das ist komplett unabhängig davon, ob man die Verwaltung einem
malloc() überlässt oder statisch vorbelegt (bei letzterem wird der
Fall sogar mit großer Wahrscheinlichkeit schon eher eintreten).

Wenn du ein rein statisch lösbares Problem hast, stellt sich die Frage
eigentlich gar nicht. ;-)

: Bearbeitet durch Moderator
von Bitflüsterer (Gast)


Lesenswert?

Man könnte noch folgende "Daumenregeln" hinzufügen:

1. Auf einem uC ist der Speicherbedarf bei den allermeisten Anwendungen 
von vorneherein bekannt oder es kann ein Maximalbedarf benannt werden. 
Dann würde man eher ein Array definieren.
2. Falls dieses Array nicht per se wie ein Ringpuffer oder 
Kellerspeicher bedient wird, schreibt man sich eine eigene kleine 
Verwaltung für die Einträge (ein paar Zeiger oder/und ein Flag in einer 
Struktur die dann in dem Array gespeichert wird).
3. Falls man aber viele solcher Arrays mit kleinen Bereichen für die 
Speicherung der Anwendungsdaten braucht dann gibt es einen Grenzwert in 
dem die eigene Verwaltung genauso viel Overhead erzeugt wie die von 
malloc/realloc. Dann kann man 2. ebensogut sein lassen.
4. Auf einem uC allerdings wird man einigen Aufwand in Vorüberlegungen 
stecken um malloc/realloc zu vermeiden.

5. Auf einem PC hingegen ist der Speicherbedarf oft nicht bekannt. 
Insbesondere die Abfolge der nötigen Anforderungen ist oft nicht bekannt 
(im gegensätzlichen Fall könnte man einige logisch getrennte Variablen 
zusammenlegen, da sie immer zeitlich und kausal zusammenhängend 
gespeichert werden müssen).
6. Auf einem PC ist Speicher meist im Überschuss vorhanden, so dass die 
Verwendung von malloc/realloc meist garnicht in Frage steht.
7. Allerdings gibt es auch Anwendungen für den PC die den Speicher voll 
ausnutzen (z.B. Bild- oder Videobearbeitung) und in denen Swapping aus 
Zeitgründen vermieden werden soll. Dann greifen die unter 3. bzw. 5. 
genannten Maßnahmen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitflüsterer schrieb:
> 2. Falls dieses Array nicht per se wie ein Ringpuffer oder
> Kellerspeicher bedient wird, schreibt man sich eine eigene kleine
> Verwaltung für die Einträge (ein paar Zeiger oder/und ein Flag in einer
> Struktur die dann in dem Array gespeichert wird).

Dann bist du bei dem Punkt, den ich oben nannte: das Risiko, da Bugs
einzubringen, ist deutlich größer, als wenn man gleich malloc() nimmt.

Letztlich ist malloc() auf einem Controller auch weiter nichts als
die Verwaltung eines großen Byte-Arrays, welches sich zwischen dem
Ende der statischen Daten und dem Stack befindet.  Beim malloc() aus
der (hier relevanten) Newlib kann man es sogar vollständig so nutzen,
da man dort die Backend-Funktion _sbrk() ohnehin selbst liefern muss,
die den nächsten freien Speicherblock anfordert.

Aber auch beim statischen Array als „Heap“ hat man das Risiko, wenn
man dieses zu groß macht, dass da der Stack hineinrennt …  Nur, dass
da eben kein malloc() mehr eine 0 zurückgeben kann.

von Otto Realverbraucher (Gast)


Lesenswert?

Ich möchte sowohl Overhead minimieren, als auch die Fragmentierung des 
Speichers verhindern.

Ich habe mir vorgestellt wie ich selbst "realloc" implementieren würde: 
falls neue Länge kleiner als jetzige ist, dann nur das Delta am Ende 
freigeben und Fertig!

Nun meine Fragen: Wenn es garantiert ist, dass in der Zukunft die 
Größe/Länge des allokierten Speichers gleich bleibt oder kleiner wird,
1. bleibt die Anfangs-Adresse gleich? (ISO, C99 od. Sonstiges?)
2. werden die nicht mehr benötigten Bytes ("Delta") am Ende freigegeben?
1
/*****************************************
2
        Am Anfang (nach "setup()" Aufruf.)
3
Addr.   ---------------------
4
0x0815  |0|1|2|3|4|5|6|7|8|9|
5
        ---------------------
6
7
        Nach aufruf von "realloc(5, sizeof(uint8_t))" ?
8
Addr.   ---------------------
9
0x????  |0|1|2|3|4|freigeg.?|
10
        ---------------------
11
******************************************/
12
13
#define MAX_SIZE 10
14
15
uint8_t* ptr;
16
17
void setup()
18
{
19
   *ptr = (uint8_t*)calloc(MAX_SIZE, sizeof(uint8_t));
20
}
21
22
void loop()
23
{
24
   // Tue dies..., tue das...
25
26
   newLength = 5 ; // Neue Länge ist garantiert kleiner als MAX_SIZE.
27
28
   // Nun brauchen wir weniger Speicher.
29
   *ptr = (uint8_t*)realloc(newLength, sizeof(uint8_t));
30
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Otto Realverbraucher schrieb:
> Wenn es garantiert ist, dass in der Zukunft die Größe/Länge des
> allokierten Speichers gleich bleibt oder kleiner wird,
> 1. bleibt die Anfangs-Adresse gleich? (ISO, C99 od. Sonstiges?)
> 2. werden die nicht mehr benötigten Bytes ("Delta") am Ende freigegeben?

Nichts davon ist garantiert.

Was die avr-libc-Implementierung betrifft, handhabt sie das im Prinzip
so, aber den Sourcecode kannst du dir ja selbst ansehen.

: Bearbeitet durch Moderator
von Otto Realverbraucher (Gast)


Lesenswert?

Jörg W. schrieb:

> Nichts davon ist garantiert.
>
> Was die avr-libc-Implementierung betrifft, handhabt sie das im Prinzip
> so, aber den Sourcecode kannst du dir ja selbst ansehen.

Vielen Dank Jörg W.!

Um zu garantieren, dass die anderen globale Variablen im Heap, alle nach 
dem allokierten Bereich platziert werden, bringt es was den allokierten 
Array als Erstes zu definieren und Speicherplatz zuweisen (statisch)?

z.B.:

uinit8_t * ptr = (uint8_t*)malloc(MAX_SIZE * sizeof(uint8_t));
bool b = false;
char c = 'c';
int16_t i = 0;
...
usw.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich denke nicht, dass solche Verrenkungen irgendwelcher Art wirklich
was bringen.

Entweder hast du ein Problem, welches sich mit statisch belegtem
Speicher erledigen lässt, d. h. alle Details sind zur Compilezeit
bekannt.  Dann brauchst du kein malloc().  Oder du hast wirklich
dynamische Daten, weißt zur Compilezeit also nicht, was wirklich wann
auf dich zukommt.  Dann brauchst du 1.) immer (egal ob malloc()
oder nicht) eine „Exit-Strategie“, also irgendeine Maßnahme, die du
dann ergreifen kannst, wenn die Datenflut größer wird, als deine
Speichermöglichkeiten es hergeben.  2.) brauchst du dann einen
Allokator, bspw. eben malloc().

Bei wirklich zufälligen Anforderungen an die Größe der jeweiligen
Speicherblöcke ist die Gefahr von Fragmentierung nicht so groß, wie
das gemeinhin angenommen wird.  Sofern überhaupt noch ein paar
Reserven da sind, gleicht sich das über die Programmlaufzeit wieder
aus.

Deine Variablen „ptr“, „b“, „c“ und „i“ spielen dabei nur insofern
eine Rolle, als dass sie beim Eintritt in die Funktion Stack belegen
könnten (oft genug wird sie der Compiler ohnehin in Registern
halten).  Der für den Stack benutzte Platz steht dem Heap nicht mehr
zur Verfügung, der ist also schon weg, bevor dein malloc() da
überhaupt aufgerufen wird.

: Bearbeitet durch Moderator
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.