Vorab: Ich weiss schon was mir die Warnung sagen will, und wie man sie
wegkriegt, nur möchte ich das in diesem Fall vermeiden!
Es geht um einen "Treiber" für einen Sensor in C (gcc) auf AVR. Der
Treiber hat im Wesentlichen eine init() und eine read() Funktion. im
Init werden eine Reihe von (festen Kalibrierungs-) Paramtern vom sensor
per i2c gelesen, mit diesen wird dann im read() das Ergebnis berechnet
bzw. korrigiert.
Der Pseudo-Code sieht so aus:
Im main wird zuerst einmal Drv_init() aufgerufen, und dann jede Sekunde
Drv_read(). Allerdings nur wenn init() überhaupt erfolgreich war.
Nun krieg ich im read() die Warnungen dass param1, 2, ... möglicherweise
nicht initialisiert wären.
Ursache dafür ist der "early return" im init, wenn die i2c-Kommunikation
fehlschlägt. Grundsätzlich hat der GCC ja recht damit, und ich staune
nicht schlecht auf welche gefinkelten Sachen der draufkommt!
Nur - in meinem Fall spielt das wenig bis keine Rolle; wenn das init
fehlschlägt, macht das read eh keinen Sinn (und wird auch nicht
aufgerufen, und wenn doch ist der Caller selbst schuld :-)
Eine Möglichkeit die Warnung zu beseitigen, wäre alle 12 Werte vorher
(oder im "early return") auf 0.0 zu setzen. 12 floats zu initialisieren
braucht aber recht viel asm-code....
Es reicht auch die 12 Werte (die eh in einer eigenen struct liegen) per
memset() auf 0 zu setzen, interessanterweise "kneisst" der GCC sogar
das. Resultierend sind ein paar wenige ASM-zeilen.
So richtig glücklich bin ich damit aber auch nicht...
Die Warnung generell abschalten kommt auf keinen Fall in Frage.
Kreative Ideen?
Mark Brandis schrieb:> Alte MISRA-Weisheit: Eine Funktion braucht überhaupt nur eine> return-Anweisung.
Obs eine Weisheit ist, lass ich mal dahingestellt :-) aber das ändert an
der Warnung genau gar nix.
Dann erkennt der Compiler offenbar nicht, dass durch den Funktionsaufruf
die "bewarnten" Variablen auf vernünftige Werte gesetzt werden.
Spricht etwas dagegen, dies im Fehlerfall händisch zu machen?
Ich hätte nicht erwartet, dass gcc so sensibel ist.
An dieser Stelle sehe ich keinen Grund für eine Warning, da die
Variable, auf die zugegriffen wird, ein Funktionsparameter ist. Das kann
dann alles mögliche sein.
Wenn du das in der Init auf 0 initialisierst, verhindert das nicht, dass
es in der Read nicht initialisiert sein könnte.
Eine solche Warning könnte ich mir nur erklären, wenn du tiefgreifende
Code-Checks ausführen würdest, wo die Tools dazu aber einiges kosten!
Und selbst dann dürfte keine Warning kommen, denn dann würde das tool
die fallunterscheidung in deiner main beachten.
Vieleicht hat das aber auch was mit optimierungen zu tun, aber es wäre
mir neu, wenn ein compiler über mehrere c-files hinweg optimieren würde.
Mark Brandis schrieb:> Alte MISRA-Weisheit: Eine Funktion braucht überhaupt nur eine> return-Anweisung.>
Da steht doch bestimmt auch, dass man den code nicht unnötig
verkomplizieren soll und variablen möglichst früh initialisier soll
(würde ich da zumindest rein schreiben ;-) :
Michael Reinelt schrieb:> val += Drv->param1; // <== Warning> val *= DRV->param2; // <== Warning
Das ist schonmal ganz großer Mist.
Man unterscheidet Variablen niemals nur durch die Schreibweise. Bei
sowas sucht man sich dumm und dämlich.
Und Warnungen immer im Klartext (copy&paste) mit Quellcode als Anhang,
damit man die Zeilennummer zuordnen kann.
Die DRV bzw. drv werden ja wohl irgendwo angelegt werden und dann da
einfach initialisieren. Das macht dann einmalig der Startup-Code.
Michael Reinelt schrieb:> Nun krieg ich im read() die Warnungen dass param1, 2, ... möglicherweise> nicht initialisiert wären.
Da Du Dich ja schon bei DRV/Drv verschrieben hast, kannst Du uns nicht
mal den "richtigen" Code zeigen und die echte Warning? Typischer Weise
würde der Compiler wohl eher wegen einem nicht initialisiertem "val"
warnen.
Ansonsten: Warnungen die Buggy sind, würde ich abschalten und nicht
drumherum programmieren und das ganze dann im Code mit einem Kommentar
versehen.
Mark Brandis schrieb:> Spricht etwas dagegen, dies im Fehlerfall händisch zu machen?
Ja, wie im Ausgangspost beschrieben, ich habe keine Lust 12 floats zu
initialisieren, und mir gefällt das memset() nicht sonderlich.
Little Basdart schrieb:> An dieser Stelle sehe ich keinen Grund für eine Warning, da die> Variable, auf die zugegriffen wird, ein Funktionsparameter ist. Das kann> dann alles mögliche sein.
Doch, die Warnung ist gerechtfertigt.
> Wenn du das in der Init auf 0 initialisierst, verhindert das nicht, dass> es in der Read nicht initialisiert sein könnte.
Doch, der GCC weiss das.
> Eine solche Warning könnte ich mir nur erklären, wenn du tiefgreifende> Code-Checks ausführen würdest, wo die Tools dazu aber einiges kosten!> Und selbst dann dürfte keine Warning kommen, denn dann würde das tool> die fallunterscheidung in deiner main beachten.
Unterschätze niemals den GCC!
> Vieleicht hat das aber auch was mit optimierungen zu tun, aber es wäre> mir neu, wenn ein compiler über mehrere c-files hinweg optimieren würde.
Such mal nach "LTO", der GCC kann das schon lange!
Torsten Robitzki schrieb:> Εrnst B✶ schrieb:>> Welchen Startwert hat val?>> wird durch i2c_read_anything() gesetzt.
Vermutest du. Ich hab den Quelltext dazu nicht gesehen, und der Compiler
auch nicht (wenn er z.B. aus einer Library/seperatem Soucre-File kommt)
Michael Reinelt schrieb:> Mark Brandis schrieb:>> Spricht etwas dagegen, dies im Fehlerfall händisch zu machen?>> Ja, wie im Ausgangspost beschrieben, ich habe keine Lust 12 floats zu> initialisieren
Keine Lust ist nicht wirklich ein starkes Argument. :-)
Mark Brandis schrieb:> Michael Reinelt schrieb:>> Mark Brandis schrieb:>>> Spricht etwas dagegen, dies im Fehlerfall händisch zu machen?>>>> Ja, wie im Ausgangspost beschrieben, ich habe keine Lust 12 floats zu>> initialisieren>> Keine Lust ist nicht wirklich ein starkes Argument. :-)
Doch :-) nein, im Ernst, ich bin mir einfach nur unsicher wie die
"saubere" Lösung aussehen könnte. memset() ist schön klein (auch im
resultierenden asm-output) aber eher dirty. 12 Floats zu setzen wird
(vermutlich) eher viel asm-output erzeugen (48 mov's?)
Nachdem hier keiner über Pseudo-Code nachdenken mag, häng ich den
kompletten Code an.
SO sieht eine der Warnungen aus (insgesamt 12 Stück, alle im
wesentlichen identisch)
1
bmp280.c:145:11: warning: ‘BMP280.calib.T1’ may be used uninitialized in this function [-Wmaybe-uninitialized]
2
float ofs = (float) T_raw - BMP280->calib.T1;
3
^
4
main.c:63:14: note: ‘BMP280.calib.T1’ was declared here
Michael Reinelt schrieb:> Weil T_raw ein int ist. Du hast recht, BMP280->calib.T1 ist ein float,> damit würde T_raw implizit gecasted, aber: hilfts nix, schadt's nix...
auch wenn BMP280->calib.T1 ein ist währe, bräuchte man keinen cast.
Cast sind böse, wenn sie mal die Datentypen ändert, bekommt man fehler
nicht mehr mit.
Michael Reinelt schrieb:> 12 Floats zu setzen wird (vermutlich) eher viel asm-output erzeugen
Für nicht genutzte Rechenzeit gibt es kein Geld zurück :-)
Michael Reinelt schrieb:> memset() ist schön klein (auch im resultierenden asm-output) aber eher> dirty.
Was ist daran dirty?
Ein Bitmuster nur aus Nullen ist in IEEE 754 seit eh und je eine
eindeutige Null. Sofern du nicht gerade auf Architekturen portabel
bleiben willst, die kein IEE 754 benutzen, ist das also absolut
kein Problem.
Was anderes würde der Startup-Code ja auch nicht tun, um irgendwelche
Gleitkommavariablen im BSS auf 0.0 zu setzen.
Peter II schrieb:> Cast sind böse
Nein :-)
Peter II schrieb:> auch wenn BMP280->calib.T1 ein int wäre, bräuchte man keinen cast.
Doch, unter Umständen schon:
1
intmain(void)
2
{
3
inta=1<<30;
4
intb=1<<30;
5
doublex=a+b;
6
doubley=(double)a+b;
7
printf("a=%d b=%d x=%f y=%f\n",a,b,x,y);
8
}
Aber: Das hat mit dem Thema hier absolut nichts zu tun!
Mark Brandis schrieb:>> 12 Floats zu setzen wird (vermutlich) eher viel asm-output erzeugen>> Für nicht genutzte Rechenzeit gibt es kein Geld zurück :-)
Es geht in dem Fall nicht um Rechenzeit, sodern um (asm-) Code size.
Jörg Wunsch schrieb:> Was ist daran dirty?
das #include <string.h> ? :-)
> Ein Bitmuster nur aus Nullen ist in IEEE 754 seit eh und je eine> eindeutige Null. Sofern du nicht gerade auf Architekturen portabel> bleiben willst, die kein IEE 754 benutzen, ist das also absolut> kein Problem.>> Was anderes würde der Startup-Code ja auch nicht tun, um irgendwelche> Gleitkommavariablen im BSS auf 0.0 zu setzen.
Ok, überzeugt. Danke!
Trotzdem - gäbs noch eine andere, schöne Alternative? Angenommen meine
12 floats wären nicht so schön hintereinander in einer struct?
Markus schrieb:> Wie wäre es mit folgender init():
Vermutlich dasselbe in Grün: falls die i2c-Kommunikation schon ganz am
Anfang versagt, werden die Kalibrierungsregister nie gelesen, C.* ist
uninitialisiert.
Abhilfe wäre auch hier ein memset() auf C
Aber ich bin eher ein Freund vom "early return" (auch wenn MISRA was
anderes sagt...)
Stimmt, dann aber wegen der variablen C.
Alternativvorschlag:
Mach die Variable BMP280 in der main zu einer globalen, dann wird sie
vom Startup code mit Nullen initialisiert.
>> ;-)
Danke, danach habe ich gesucht :-)
Markus schrieb:> Alternativvorschlag:> Mach die Variable BMP280 in der main zu einer globalen, dann wird sie> vom Startup code mit Nullen initialisiert.
Das ginge, ist aber unschön: Wenn man die Variable dann irgendwann doch
wieder lokal haben möchte, hagelts plötzlich Warnungen.
Tom schrieb:> Michael Reinelt schrieb:>> Allerdings nur wenn init() überhaupt erfolgreich war.>> Wo steht diese Bedingung?
Guter Einwand!
@ Michael Reinelt (fisa)
>Eine Möglichkeit die Warnung zu beseitigen, wäre alle 12 Werte vorher>(oder im "early return") auf 0.0 zu setzen. 12 floats zu initialisieren>braucht aber recht viel asm-code....
Ah herje. Was soll denn diese Mikrooptimierung? Intialisier den Kram
ordentlich und gut!
Falk Brunner schrieb:> Ah herje. Was soll denn diese Mikrooptimierung? Intialisier den Kram> ordentlich und gut!
Ja eh... wird jetzt per memset() generell initialisiert, und zusätzlich
hab ich noch einen "Verriegelung" eingebaut, dass bei fehlgeschlagener
initialisierung das read() erst gar nicht probiert.
Danke an alle!
Michael Reinelt schrieb:> Wenn man die Variable dann irgendwann doch> wieder lokal haben möchte, hagelts plötzlich Warnungen.
Dann mach sie eben static.
genügen. Damit wird der Compiler trotzdem noch warnen, wenn in dem
betreffenden Codeabschnitt eine Variable gelesen wird, die ganz sicher
uninitialisiert ist.
Ich persönlich hätte aber auch keine allzu großen Bedenken, die
maybe-uninitialized-Warnungen global abzuschalten, falls sie öfters zu
Fehlalarmen führen. -Wuninitialized hingegen sollte immer aktiv sein.
Wenn man mit float um sich schmeißt, spielt Code und Zeit eh keine
Rolle.
Schon jede float Operation ist erheblich teurer, als ein memset.
Der AVR hat ja keine FPU, muß also alles emulieren.
Peter Dannegger schrieb:> Wenn man mit float um sich schmeißt, spielt Code und Zeit eh keine> Rolle.
Peter, nur weil du keine Floats gebrauchen kannst, heißt das nicht,
dass sie generell nutzlos wären und auch nicht, dass man nicht trotzdem
versuchen sollte, den Platzbedarf zu optimieren.
Diese allgemeine Float-Allergie nervt ein wenig.
Peter Dannegger schrieb:> Wenn man mit float um sich schmeißt, spielt Code und Zeit eh keine> Rolle.
Da hab ich momentan gute Gegenargumente, eh konkret an dem Beispiel:
Beitrag "Bosch BMP280: Umrechnungen vereinfachen?"
kurz zusammengefasst:
int32: 930 Bytes Code, 2960 Takte
int64: 1366 Bytes Code, 6900 Takte
float: 578 Bytes Code, 3300 Takte
wobei die int32-Variante recht ungenau ist, und für gewisse Fälle
(genaue Höhenmessung) damit unbrauchbar.
der float-Code hat den kleinsten footprint, und ist doppelt so schnell
wie die int64-Variante, bei identischer Genauigkeit.
Michael Reinelt schrieb:> kurz zusammengefasst:> int32: 930 Bytes Code, 2960 Takte> int64: 1366 Bytes Code, 6900 Takte> float: 578 Bytes Code, 3300 Takte
Da fehlt aber noch der Verbrauch für die Libs.
Daß int32 größer ist, liegt daran, daß oft geinlined wird, anstatt die
Lib aufzurufen.
Daß int64 länger braucht, liegt daran, daß es auch 64Bit rechnen muß und
nicht nur 24Bit.
Jörg Wunsch schrieb:> Diese allgemeine Float-Allergie nervt ein wenig.
Da hast Du mich gründlich mißverstanden, ich benutze auch float.
Ich meinte das nur in dem Kontext, float nehmen und sich dann über das
popelige memset zu beschweren.
Peter Dannegger schrieb:> Daß int64 länger braucht, liegt daran, daß es auch 64Bit rechnen muß und> nicht nur 24Bit.
Das wiederum ist aber eben kein Argument für int64, sondern eins
für float. Wenn man einen weiten Dynamikbereich abdecken muss, dann
ist Gleitkomma einfach das Mittel der Wahl. Die 24 Bit Genauigkeit
selbst genügen ja offenbar hier vollauf, nur der Dynamikbereich von
int32 genügt nicht.
Peter Dannegger schrieb:> float nehmen und sich dann über das popelige memset zu beschweren.
OK, er hat ja akzeptiert, dass memset() durchaus auch dafür legitim
sein kann.
Eine Funktion
void arrayset(void *array, size_t nele, union anytype initvalue);
hat der C-Standard halt vergessen zu normieren.
Peter Dannegger schrieb:> Ich meinte das nur in dem Kontext, float nehmen und sich dann über das> popelige memset zu beschweren.
ich gestehe, es war neu für mich, dass memset ganz sauber zum
initialisieren von float geeignet ist. Wieder was gelernt!
Jörg Wunsch schrieb:> Die 24 Bit Genauigkeit> selbst genügen ja offenbar hier vollauf, nur der Dynamikbereich von> int32 genügt nicht.
Vollkommen richtig, und der Original-Code von Bosch zeigt auch sehr
schön, wie man sich ins Knie schießen kann, wenn man verzweifelt
versucht den Dynamikbereich doch noch irgendwie hinzumurksen, indem man
in einem fort hin- und herschiftet.
Jörg Wunsch schrieb:> Eine Funktion>> void arrayset(void *array, size_t nele, union anytype initvalue);>> hat der C-Standard halt vergessen zu normieren.
Ich bevorzuge das Initialisieren im Startup-Code:
Peter Dannegger schrieb:> Ich bevorzuge das Initialisieren im Startup-Code:
Ja, versuch ich normalerweise auch so zu machen.
In diesem Fall gehts aber um "interne" Felder (im ++-Jargon würde man
vermutlich "private" sagen), die außerhalb des Treibers eigentlich nix
verloren haben.
Michael Reinelt schrieb:> In diesem Fall gehts aber um "interne" Felder
Da kann man das doch auch machen.
Die Frage ist dann, ob diese Funktion mehrmals aufgerufen wird und dann
jedesmal neu initialisiert werden muß.
Wenn nicht, dann kann man das Array static machen und es wird nur einmal
vom Startup-Code initialisiert:
Peter Dannegger schrieb:> Die Frage ist dann, ob diese Funktion mehrmals aufgerufen wird und dann> jedesmal neu initialisiert werden muß.> Wenn nicht, dann kann man das Array static machen und es wird nur einmal> vom Startup-Code initialisiert:
Nö, das geht leider nicht so einfach, da ich die ganze struct (und
speziell diese Kalibrierungsparameter) mehrfach halten muss, da man auch
mehrere Sensoren per i2c anschließen kann (und ich brauch in meiner
speziellen Anwendung auch schon zwei davon)
Deswegen auch die ganze Übergabe der Struct. Sonst würd ich das eh
Treiber-Intern machen.
Die "schöne" Variante wäre dass sich der Treiber selbst die Struktur
allokiert und nur einen Handle zurückgibt. Hieße aber mehr oder weniger
malloc(), und dazu hab ich am AVR keine Lust :-)
Michael Reinelt schrieb:> Hieße aber mehr oder weniger malloc(), und dazu hab ich am AVR keine> Lust :-)
Funktionieren würde es, problemlos. Das lästigste daran dürfte der
Flash-Verbrauch für malloc() selbst sein.
Michael Reinelt schrieb:> Nö, das geht leider nicht so einfach
Warum nicht?
Der Komplexität und Anzahl der Structs sind keine Grenzen gesetzt.
Wenn die Initialisierungen für mehrere Structs gleich sind, kann man sie
in ein Macro packen und dann das Macro entsprechend oft aufrufen.
Man kann so bequem überall Default-Werte reinschreiben und später nur
die Unterschiede ändern.
Die einzige Einschränkung, die Anzahl der Structs muß zur Compilezeit
bekannt sein.
Jörg Wunsch schrieb:> Funktionieren würde es, problemlos. Das lästigste daran dürfte der> Flash-Verbrauch für malloc() selbst sein.
Ich bin bis jetzt nie in die Verlegenheit gekommen es zu brauchen. ich
hoffe das bleibt noch eine Zeitlang so...
Peter Dannegger schrieb:> Die einzige Einschränkung, die Anzahl der Structs muß zur Compilezeit> bekannt sein.
Ja, darüber habe ich auch nachgedacht.
Nachdem im Regelfall eh nicht mehr als zwei angeschlossen sein können
(es können nur zwei verschiedene i2c-Adressen per Hardware ausgewählt
werden) wäre es vermutlich sogar am einfachsten, gleich zwei statische
structs zu verwenden, und sogar die i2c-Adressen hart vorzugeben).
Aber für meine derzeitige Anwendung ist die momentane Variante
ausreichend. Wenn man den Code ernsthaft veröffentlichen wollte, sollte
man darüber nochmal nachdenken.
Michael Reinelt schrieb:> Ich bin bis jetzt nie in die Verlegenheit gekommen es zu brauchen. ich> hoffe das bleibt noch eine Zeitlang so...
Ich hab's mal (vor Jahren) geschrieben, und nachdem einige Bugs im
Laufe der Jahre gefixt worden sind und es stabil durch die Testsuite
läuft, habe ich ausreichend Vertrauen in die Implementierung. ;-)
Jörg Wunsch schrieb:> Michael Reinelt schrieb:>> Ich bin bis jetzt nie in die Verlegenheit gekommen es zu brauchen. ich>> hoffe das bleibt noch eine Zeitlang so...>> Ich hab's mal (vor Jahren) geschrieben, und nachdem einige Bugs im> Laufe der Jahre gefixt worden sind und es stabil durch die Testsuite> läuft, habe ich ausreichend Vertrauen in die Implementierung. ;-)
Gerade durchgesehen: Schaut doch eh einfach aus :-) ist ja nicht mal ein
buddy allocator (nein, nicht schlagen!)
Was davon braucht so viel flash?
Michael Reinelt schrieb:> Was davon braucht so viel flash?
Der Code selbst halt, ein paar Zeilen sind's schon, und eben viele
Fallunterscheidungen.
malloc() und free() sind auch nicht getrennt, weil ich natürlich
davon ausgehe dass, wer malloc() benutzt, den Speicher ohnehin auch
irgendwann wieder freigeben würde. ;-) malloc() und free() brauchen
je um die 300 Byte für ihre Implementierung.
Daniel A. schrieb:> Wie funktioniert malloc auf ucs eigentlich?
Vermutlich nicht sehr viel anders, als es vor 40 Jahren auf einer
PDP-11 funktioniert hat.
Halt weit weniger kompliziert als sonstige heutige Algorithmen, die
vorrangig auf hohe Performance getrimmt sind auf Systemen mit
virtuellem Speicher, bei denen es auf ein paar Bytes mehr nicht ankommt.
> Woran erkennt es/
In der AVR-Implementierung: durch Vergleich gegen den Stackpointer
abzüglich einer (einstellbaren) Sicherheitsreserve, oder durch Vergleich
gegen eine vorab vorgegebene Grenze. RTFDoc. :)
> was passier> wenn der Speicher voll ist?
Es gibt NULL zurück, so, wie es der Standard vorsieht.
> Ich dachte immer malloc auf ucs wäre gefährlich wegen> Speicherfragmentierung usw.
Ja, natürlich, Speicherfragmentierung ist ein gewisses Risiko, wenn
die äußeren Umstände der Anwendung es dazu bringen. Mit anderen
Worten: du wirst immer einen pathologischen Fall konstruieren können,
an dem du nachweisen kannst, dass man den Speicher fragmentiert
bekommt. Die Frage ist nur, ob die tatsächliche Anwendung denn
genau so in ihren Anforderungen ist. Grob gesagt, je besser die
Anforderungen in ihrer Größe statistisch verteilt sind, um so
weniger wahrscheinlich ist eine Fragmentierung (mal vom Trivialfall
abgesehen, dass alle Requests gleich groß sind, dann fragmentiert
natürlich nie etwas).
Wenn du ein wirkliches dynamisches Problem hast (Nachrichten
vorher unbekannter Länge entstehen in mehr oder weniger zufälligen
Abständen), dann musst du so oder so das Problem lösen, und du musst
auch stets damit umgehen können, dass dir in so einer Situation der
Speicher ausgehen kann. Damit muss eine entsprechende Applikation
irgendwie umgehen können.
Ein statisch vorbelegtes Array wird diesen Zustand nur sehr
wahrscheinlich bereits viel eher erreichen als die malloc-Variante
(dafür braucht's weniger Codegröße). Außerdem macht es dann nicht
einmal einen Versuch, eine Überallozierung zu erkennenn (Stack
wächst in das vorbelegte Array rein, da es zu groß angelegt worden
ist).
(Wenn du kein dynamisches Problem hast, brauchst du auch kein
malloc().)
> Deshalb hab ich diese alternative geschrieben:> https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/mempool.c
Sorry, no documentation found.
Das Einzige, was man natürlich allemal besser implementieren könnte
als in malloc() wäre ein handle-basierter Allokator, der sich selbst
bei Bedarf defragmentieren kann. Dafür wird bei einem solchen die
Applikation etwas aufwändiger, denn sie muss sich zum Handle dann
jeweils die aktuell gültige Adresse abholen.
Jörg Wunsch schrieb:> Sorry, no documentation found.
Ja, das sollte ich mal erledigen. Aber wo fang ich da blos an...
> Das Einzige, was man natürlich allemal besser implementieren könnte als> in malloc() wäre ein handle-basierter Allokator, der sich selbst bei> Bedarf defragmentieren kann. Dafür wird bei einem solchen die> Applikation etwas aufwändiger, denn sie muss sich zum Handle dann> jeweils die aktuell gültige Adresse abholen.
Das ist bei meiner Alternative der fall. Es erstellt in einem Array eine
verkettete liste, die einen Pointer auf einen Pointer enthält, der auf
den anfang der Daten nach den Informationen über den Listeneintrag
zeigt. Der Allocator bekommt einen Pointer auf den Pointer dessen Wert
dieser bei bedarf anpasst, damit er auf die Daten zeigt.
Vorteil: 1) Automatische Speicherdefragmentierung.
Nachteil:
1) Der Pointer auf die Daten muss bis zum freigeben an der selben
Speicherstelle gespeichert werden.
2) Der Zugriff auf die Daten muss meistens über einen Pointer auf den
Datenpointer erfolgen, da sich dessen wert ändern kann.