Hallo zusammen,
ich habe eine Funktion callee(), bei der Daten zwischen den Aufrufen
"gemerkt" werden müssen. Aufgrund der schieren Datenmenge und weil sie
später nicht mehr benötigt werden, will ich diesen Daten nicht dauerhaft
Speicherplatz mit static geben oder eine globale Variable daraus machen.
Aufgrund der Struktur der aufrufenden Funktion caller() muss sie eine
void-void-Funktion bleiben. Also muß ich den Speicherplatz in der
aufrufenden Funktion reservieren und später wieder freigeben.
Dabei kann ich mir zwei Varianten vorstellen:
a) Mittels automatischen Variablen
b) Mittels malloc()/free()
Im Pseudo-Quelltext sähe das so aus:
1
typedefstruct
2
{
3
uint64_ta,b,c,d,e,f,g,h,i,j,k,l,m,n;
4
}
5
SomeLargeStruct_t;
6
7
8
SomeLargeStruct_t*LargeStructPtr;
9
10
11
/* Callee function needs to be void-void function, will only be called by caller() */
12
voidcallee(void)
13
{
14
assert(LargeStructPtr!=NULL);
15
SomeLargeStruct_t*Data=LargeStructPtr;
16
17
//Do something you like with it [...]
18
19
}
20
21
22
/* caller() cannot be changed */
23
voidcaller(void)
24
{
25
// [...] I will do some complicated stuff here
26
callee();
27
// [...] I will do some more complicated stuff
28
}
29
30
31
/* Automatic variable */
32
voidfooAuto(void)
33
{
34
SomeLargeStruct_tData;
35
LargeStructPtr=&Data;
36
37
while(manyManyIterations)
38
{
39
caller();
40
}
41
42
LargeStructPtr=NULL;
43
}
44
45
46
47
/* Malloc */
48
voidfooMalloc(void)
49
{
50
LargeStructPtr=malloc(sizeof(SomeLargeStruct_t));
51
52
while(manyManyIterations)
53
{
54
caller();
55
}
56
57
free(LargeStructPtr);
58
}
Vorausgesetzt, dies ist nicht der einzige Aufruf von malloc() im
gesamten Programm (d.h. ich spare schon einmal nicht diesen Teil der
stdlib): Hat eine dieser Varianten Vorteile, oder ist das
Geschmackssache?
Walter T. schrieb:> will ich diesen Daten nicht dauerhaft Speicherplatz mit static geben> oder eine globale Variable daraus machen.
Verwende einen /static/-Pointer in Deiner Funktion um das Ergebnis von
malloc aufzuheben. Ist der Pointer NULL, muss malloc aufgerufen werden,
ist er nicht NULL, kann der Speicher verwendet werden.
Du brauchst jetzt nur noch eine Möglichkeit schaffen, den Speicher nach
Gebrauch wieder loszuwerden, d.h. Du musst Deine Funktion so aufrufen
können, daß sie erkennt, daß sie den Speicher mit free freigeben soll.
Ich würde die automatische Variable nehmen:
- Kein malloc () nötig
- Keine Chance, free () zu vergessen
Bevorzugen würde ich allerdings eine Variante,
die den Zeiger als Funktionsparameter durchreicht.
Deine Erklärung in dem Punkt ist mir schleierhaft,
sorry :-)
Bärenmarke schrieb:> Ich würde die automatische Variable nehmen:
Lebt nur während der Laufzeit der Funktion und enthält beim nächsten
Aufruf nicht die Daten des letzten Durchlaufs, was aber gefordert wird:
Walter T. schrieb:> bei der Daten zwischen den Aufrufen "gemerkt" werden müssen.
Braucht obendrein Platz auf dem Stack, und zwar offensichtlich viel :
> Aufgrund der schieren Datenmenge ...
Walter T. schrieb:> Aufgrund der Struktur der aufrufenden Funktion caller() muss sie eine> void-void-Funktion bleiben.
Versuch diese verpfuschte Funktion umzustrukturieren.
Rufus Τ. F. schrieb:> Lebt nur während der Laufzeit der Funktion und enthält beim nächsten> Aufruf nicht die Daten des letzten Durchlaufs, was aber gefordert wird:>> Walter T. schrieb:>> bei der Daten zwischen den Aufrufen "gemerkt" werden müssen.
Ich bin vom Pseudocode ausgegangen. Die Struct "lebt" im Stack von
fooAuto().
Rufus Τ. F. schrieb:> Braucht obendrein Platz auf dem Stack, und zwar offensichtlich viel :>>> Aufgrund der schieren Datenmenge ...
Platz braucht es überall, aufgrund der schieren Datenmenge :-)
Ich wuerde eine globale Array Variable verwenden, die hinreichend gross
ist um alles zu beinhalten. Das erspart irgendwelche dynamischen
Speicherreservierungen
Walter T. schrieb:> Hat eine dieser Varianten Vorteile, oder ist das> Geschmackssache?
Der Stackgröße ist häufig "begrenzt". Auf einigen Architekturen
tatsächlich, auf anderen durch das OS mit (einstellbaren) Hard- und
Softlimits.
Dass das ein UglyHack™ ist, wissen wir wohl alle. Wenn's aber nicht
anders geht, würde ich immer die auto-Variante wählen, evtl noch mit
einem zusätzlichen assert(Ptr==NULL) in fooAuto. Die malloc-Variante
bieten keinerlei Vorteile, braucht nur zusätzlichen Speicher, mehr
Rechenzeit und fragmentiert den Heap.
Bärenmarke schrieb:> Bevorzugen würde ich allerdings eine Variante,> die den Zeiger als Funktionsparameter durchreicht.
Ich auch. Aber callee() ist eine callback-Funktion, deren Signatur durch
das aufrufende System bestimmt wird. Wie eine ISR.
foobar schrieb:> Dass das ein UglyHack™ ist, wissen wir wohl alle.
Warum eigentlich? Wie sieht die saubere Variante aus, wenn
Callback-Funktionen oder ISRs und ihre Variablen nur in einem genau
bestimmten, zeitlich begrenzten Rahmen benötigt werden?
Walter T. schrieb:> Ich auch. Aber callee() ist eine callback-Funktion, deren Signatur durch> das aufrufende System bestimmt wird. Wie eine ISR.
D.h. du weißt ganz genau wann die Struktur benötigt wird. Schon Mal gut.
Die eine Frage die sich mir stellt: Machst du was sinnvolles mit dem
nicht verwendeten Speicher?
Du musst ihn so wie so irgendwo vorhalten. Sei es durch einen
entsprechend großen Stack oder einen entsprechend großen Heap oder eine
globale Variable.
Der Fall bei dem sich die dynamische allokierung lohnt, ist wenn man
mehrere ähnliche Strukturen zu unterschiedlichen Zeiten braucht. Aber
auch das ließe sich mit Unions statisch lösen. Oder halt wenn der
Speicherbedarf an Stack deines Programms später noch in eine ähnliche
Größenordnung anwächst.
Obwohl ich kein großer Fan von sehr großen Datenmengen auf dem Stack
bin, würde ich in deinem Fall vermutlich auch so machen, wenn ein malloc
nicht einfach verfügbar ist. Man muss sich aber der Anforderungen an den
Stack bewusst sein.
Für die malloc Variante spricht mMn die explizite Kommunikation, dass
hier etwas speicherlastiges passiert. Weiter kann bei Speichermangel die
Kontrolle über das Programm behalten werden. Wenn dein Stack überläuft
merkst du das meistens an ganz anderer Stelle im Programm. So gesehen
finde ich malloc besser.
Karl schrieb:> Die eine Frage die sich mir stellt: Machst du was sinnvolles mit dem> nicht verwendeten Speicher?
Die Frage ist gut, und die ehrliche Antwort lautet: Nein. Momentan ist
alles als dauerhafte, globale Variable angelegt und es funktioniert
alles.
Was mich daran stört: Die Funktionen, die diesen Speicherplatz belegen,
werden extrem selten benutzt. Sie werden in einem interaktiven
Nutzermenü (deswegen die fixe Signatur) verwendet, das vermutlich
einmalig beim Einrichten des Systems benötigt wird und helfen beim
Tuning einiger Parameter. Oder auch nie. Man kann auch anders
einstellen.
a) Irgendwie widerspricht es meinem Ordnungssinn, dass ein großer Teil
des Speichers von einer nie genutzten Funktion belegt wird.
b) Sobald ich eine zweite, ähnliche Funktion schreibe, wird es wirklich
eng.
Walter T. schrieb:> Was mich daran stört: Die Funktionen, die diesen Speicherplatz belegen,> werden extrem selten benutzt. Sie werden in einem interaktiven> Nutzermenü (deswegen die fixe Signatur) verwendet, das vermutlich> einmalig beim Einrichten des Systems benötigt wird und helfen beim> Tuning einiger Parameter. Oder auch nie. Man kann auch anders> einstellen.>> a) Irgendwie widerspricht es meinem Ordnungssinn, dass ein großer Teil> des Speichers von einer nie genutzten Funktion belegt wird.
Im Prinzip ist für solche Fälle malloc()/free() der richtige Weg,
zumindest auf von Menschen bedienten Computern.
Wenn der Speicher zu knapp ist, bekommt man eine Fehlermeldung, dass das
Programm nicht ausgeführt werden kann und der Benutzer kann reagieren
(andere Programme schließen, den Rechner aufrüsten etc.).
Bei Mikrocontrollersystemen gibt es diese Möglichkeit nicht. Das Menü
muss angezeigt werden können, egal was nebenbei sonst noch passiert.
Wenn du also genau absehen kannst, dass das Menu nur dann angezeigt
wird, wenn auch 100%ig genug Speicher zur Verfügung steht, dann kann man
malloc() verwenden, um danach diesen Speicher anderweitig nutzen zu
können.
Wann das nicht sicherstellbar ist, würde ich den Speicher dauerhaft
reservieren (auf dem Stack), und der Compiler stellt sicher, dass deine
Firmware zu jeder Zeit genügend Speicher hat, um fehlerfrei ausgeführt
zu werden.
Sieht erstmal nach "Speicherverschwendung" aus, der selten genutzte
Speicher wird aber benötigt, damit das Programm immer funktioniert.
Malloc/free bringt auf nem MC nur dann was, wenn sich Funktionen
gegenseitig ausschließen, die es benutzen. Ansonsten muß Du eh den
maximal nötigen Speicher vorhalten, kannst ihn also gleich fest
vergeben.
Walter T. schrieb:
...
> Die Frage ist gut, und die ehrliche Antwort lautet: Nein. Momentan ist> alles als dauerhafte, globale Variable angelegt und es funktioniert> alles.
...
Wenn Du die volle Malloc-Funktionalität nicht brauchst, könntest Du Dir
abgespeckte Varianten anschauen. Aus dem Stegreif glaube ich mich zu
erinnern, dass z.B. Adam Dunkels lwIP und Elm Chans FAT eine eigene
Speicherverwaltung aufziehen.
Ich bin auch kein bedingungsloser Fan von dynamischer
Speicherallokierung auf kleinen Maschinen, deswegen finde ich solche
Ansätze fallweise interessant.
Walter T. schrieb:> a) Irgendwie widerspricht es meinem Ordnungssinn, dass ein großer Teil> des Speichers von einer nie genutzten Funktion belegt wird.>
Ist klar. Das geht den meisten von uns so.
> b) Sobald ich eine zweite, ähnliche Funktion schreibe, wird es wirklich> eng.
Das wäre imo DER Anwendungsfall für eine Union. Alles statisch und
trotzdem im Rahmen der Abwertung ausreichend flexibel.
Schlussendlich ist es persönlicher Geschmack. Meine Argumente pro malloc
stehen oben. Der Rest hat sicher auch entsprechende Beweggründe.
Zur Einschätzung meines Kommentars: Ich habe kommerzielle Anwendungen im
Feld, die in c++ mit dynamischem Speicher und rein virtuellen Klassen
arbeiten. Natürlich auf einem uC. Nach der Meinung einiger hier habe ich
damit den Weltuntergang ausgelöst... Tatsächlich hat diese Entscheidung
viel Arbeit bei der Entwicklung gespart weil es für meinen
Anwendungsfall natürlich war. Das kommt aber immer auf den Einzelfall
an. Will nur sagen: Keine Angst!
Peter D. schrieb:> Malloc/free bringt auf nem MC nur dann was, wenn sich Funktionen> gegenseitig ausschließen, die es benutzen.
Genau. Das ist ja hier der Fall.
Meine ursprüngliche Fragestellung war ja: "malloc() oder automatische
Variable - wo liegen die Vor- und Nachteile?" Was ja irgendwie auf die
Frage entspricht: "Stack oder Heap - wo liegen die Vor- und Nachteile?".
Meine naive Denkweise war erst einmal: Solange ich keinen Speicherschutz
und kein Betriebssystem habe, ist das egal.
Bei der automatischen Variable kann ich kein free() vergessen, dafür muß
ich von Hand dafür sorgen, daß der Zeiger auf keine ungültige Variable
zeigt. Das kommt von den Verwaltungs-Hausaufgaben auf das Gleiche
heraus.
Bei malloc() muß ich die entsprechenden Funktionen der stdlib mit
dazulinken. Ist aber sowieso schon drin, weil es an anderer Stelle
gebraucht wird. Kommt also auch aufs Gleiche heraus.
Habe ich etwas vergessen?
Joe F. schrieb:> Bei Mikrocontrollersystemen gibt es diese Möglichkeit nicht.
Naja. Display ist doch da...
> Wann das nicht sicherstellbar ist, würde ich den Speicher dauerhaft> reservieren (auf dem Stack), und der Compiler stellt sicher, dass deine> Firmware zu jeder Zeit genügend Speicher hat, um fehlerfrei ausgeführt> zu werden.
Da passt was nicht. Stack ist dynamisch und zur Compilerzeit nicht immer
oder auch überhaupt nicht zu analysieren. Den Compiler kümmert das
nicht. Du meinst vermutlich das Datensegment?
> Sieht erstmal nach "Speicherverschwendung" aus, der selten genutzte> Speicher wird aber benötigt, damit das Programm immer funktioniert.
Nö. Man Stelle sich vor es gäbe 10 dieser Menüs die aber nur exclusiv
aktiv sein können. Da ist jede der dynamischen Varianten geeignet um das
Programm überhaupt zum Laufen zu bekommen
Karl schrieb:> Joe F. schrieb:>> Bei Mikrocontrollersystemen gibt es diese Möglichkeit nicht.> Naja. Display ist doch da...
Nun würde ich bei einem Embedded-Gerät nicht unbedingt erwarten, dass
beim Versuch, ein Menü zu öffnen, die Meldung kommt, dass dafür gerade
kein Speicher frei ist.
>> Wann das nicht sicherstellbar ist, würde ich den Speicher dauerhaft>> reservieren (auf dem Stack), und der Compiler stellt sicher, dass deine>> Firmware zu jeder Zeit genügend Speicher hat, um fehlerfrei ausgeführt>> zu werden.> Da passt was nicht. Stack ist dynamisch und zur Compilerzeit nicht immer> oder auch überhaupt nicht zu analysieren. Den Compiler kümmert das> nicht. Du meinst vermutlich das Datensegment?
Ich halte von "Stack", "Heap", "Datensegement" als Bezeichnungen nicht
viel, wenn es um C geht. Keines dieser drei Dinge ist in C definiert.
Dort gibt es automatischen, dynamischen und statischen Speicher.
>> Sieht erstmal nach "Speicherverschwendung" aus, der selten genutzte>> Speicher wird aber benötigt, damit das Programm immer funktioniert.> Nö. Man Stelle sich vor es gäbe 10 dieser Menüs die aber nur exclusiv> aktiv sein können. Da ist jede der dynamischen Varianten geeignet um das> Programm überhaupt zum Laufen zu bekommen
Ja, das ist eben essenziell hier. Die ganze Idee, den Speicher nur
während der Benutzung des Menüs zu belegen, steht und fällt damit, ob zu
100% immer absolut sichergestellt ist, dass jegliche andere Benutzung
dieses Speichers nicht gleichzeitig passieren kann.
Rolf M. schrieb:> Ich halte von "Stack", "Heap", "Datensegement" als Bezeichnungen nicht> viel, wenn es um C geht. Keines dieser drei Dinge ist in C definiert.> Dort gibt es automatischen, dynamischen und statischen Speicher.
Schön. Nenn es wie du willst. Tatsache ist, dass Stack und statisch
nicht so recht zusammen passen will.
Versucht es doch Mal mit konkreten Argumenten für die eine oder andere
Variante. Bitte ohne absolutes "Bring deinen Mist in Ordnung", "alles
doof", "so wird das nie was".
Weitere Tatsache: Der Code des OP läuft. Er möchte aber was dazu lernen.
Die ewigen Besserwisser und Haaresalter sind nur begrenzt hilfreich.
Walter T. schrieb:> ich habe eine Funktion callee(), bei der Daten zwischen den Aufrufen> "gemerkt" werden müssen. Aufgrund der schieren Datenmenge und weil sie> später nicht mehr benötigt werden, will ich diesen Daten nicht dauerhaft> Speicherplatz mit static geben oder eine globale Variable daraus machen.Walter T. schrieb:> Aber callee() ist eine callback-Funktion, deren Signatur durch> das aufrufende System bestimmt wird. Wie eine ISR.
Also: Der erste Funktionsrauf knallt irgendwann unverhofft rein, dann
muss von irgendwoher Speicher organisiert werden. Speicher, der sonst
auch anderweitig verwendet werden kann. Und der ist zuuufällig beim
Funktionsaufruf frei??
Walter T. schrieb:> Peter D. schrieb:>> Malloc/free bringt auf nem MC nur dann was, wenn sich Funktionen>> gegenseitig ausschließen, die es benutzen.>> Genau. Das ist ja hier der Fall.
Da könnte man vielleicht auch über eine union nachdenken.
Malloc benötigt, daß man den Bereich des RAM für Stack und für Malloc
manuell festlegen muß.
Ist der Malloc-Bereich zu klein, dann passen nicht alle Objekte hinein.
Ist der Stackbereich zu klein, dann läuft er in den Malloc-Bereich und
zerstört dessen Struktur.
Es kann also passieren, daß ein Bereich zu klein festgelegt wurde,
obwohl in der Summe noch genügend RAM vorhanden wäre. Jede Art von
Partitionierung verschlechtert die mögliche Ausnutzung.
Peter D. schrieb:> Jede Art von> Partitionierung verschlechtert die mögliche Ausnutzung.
Okay, diese Art von Argument leutet zwar ein - aber ich verstehe es
nicht. Ich habe irgendwann einmal gelernt, daß bei den Mikrocontrollern
Stack und Heap aufeinander zu wachsen, und der berüchtigte "Stack
Overflow" dann eintritt, wenn sie aufeinander treffen. Das würde aber
bedeuten, dass es egal wäre, welcher von beiden stärker wächst und auch
kein Partitionierungsproblem auftritt.
Walter T. schrieb:> Peter D. schrieb:>> Jede Art von>> Partitionierung verschlechtert die mögliche Ausnutzung.>> Okay, diese Art von Argument leutet zwar ein - aber ich verstehe es> nicht. Ich habe irgendwann einmal gelernt, daß bei den Mikrocontrollern> Stack und Heap aufeinander zu wachsen, und der berüchtigte "Stack> Overflow" dann eintritt, wenn sie aufeinander treffen. Das würde aber> bedeuten, dass es egal wäre, welcher von beiden stärker wächst und auch> kein Partitionierungsproblem auftritt.
Jein, der Heap wird durch Fragmentierung nicht optimal genutzt darum
sollte man an der Stelle schon etwas vorausschauend planen.
Walter T. schrieb:> Ich habe irgendwann einmal gelernt, daß bei den Mikrocontrollern Stack> und Heap aufeinander zu wachsen,
Ich auch. Das ist aber, mit Verlaub, ziemlich großer Mist. Wenn auch
leider an vielen Stellen Stand der Technik.
Ein robustes malloc bekommt einen Speicherbereich zugewiesen, in dem es
arbeiten darf. Kein Speicher frei, kein erfolgreiches malloc.
Auch der Stack muss nicht in Richtung anderer Daten wachsen.
Ich hatte eben schon ziemlich viel getippt aber mein Handy hat den
Beitrag verworfen. Bei Interesse führe ich es etwas genauer aus.
Karl schrieb:> Walter T. schrieb:>> Ich habe irgendwann einmal gelernt, daß bei den Mikrocontrollern Stack>> und Heap aufeinander zu wachsen,>> Ich auch. Das ist aber, mit Verlaub, ziemlich großer Mist. Wenn auch> leider an vielen Stellen Stand der Technik.
Naja, es maximiert den nutzbaren Speicher.
> Ein robustes malloc bekommt einen Speicherbereich zugewiesen, in dem es> arbeiten darf. Kein Speicher frei, kein erfolgreiches malloc.>> Auch der Stack muss nicht in Richtung anderer Daten wachsen.
Beim AVR zumindest muss er nach unten wachsen. Und wenn er am unteren
Ende vom RAM angekommen ist, läuft er erst in die I/O-Register, dann in
die ALU-Register. Das ist nicht wirklich besser, als den Stack ans Ende
und den Heap davor zu setzen. Und beim Stack ist es sowieso nicht so
einfach wie bei malloc. Wenn keiner mehr verfügbar ist, kann man nicht
einfach NULL zurückgeben, sondern das Programm semmelt dann ab - je nach
Prozessor-Typ unkontrolliert oder durch einen Reset.
Und die Aufteilung, wie viel für beide jeweils reserviert ist, müsste
man auch für jedes Programm gesondert erstmal ermitteln.
Rolf M. schrieb:> Naja, es maximiert den nutzbaren Speicher.
Sicher? Es wird so oder so nicht mehr Speicher. Man muss nur nichts
anpassen um das eine oder andere zu nutzen.
Rolf M. schrieb:> Beim AVR zumindest muss er nach unten wachsen. Und wenn er am unteren> Ende vom RAM angekommen ist, läuft er erst in die I/O-Register, dann in> die ALU-Register. ...
Autsch. Das ist natürlich unglücklich. Da geht's dann nicht.
Cortex M erzeugt per Default einen hardfault. Dort bleibt er bis zum
watchdog oder Reset. Das ist mir lieber als ein Zombie Programm, das
später an ganz anderer Stelle abschmiert.
Karl schrieb:> Rolf M. schrieb:>> Naja, es maximiert den nutzbaren Speicher.>> Sicher?
Ja. Ueberleg doch mal wie viel Speicher maximal fuer Stack und Heap
bereitstehen, wenn
- sie aufeinander zuwachsen
- wenn sie voneinander wegwachsen
1
+------------------------+
2
| |
3
| Heap -> <- Stack |
4
| |
5
| <------ Memory ------> |
6
| |
7
| <- Heap | Stack -> |
8
| |
9
+------------------------+
Aufeinander zuwachsen ist die sinnvollste Anordnung von Stack und Heap
um den Speicher maximal nutzen zu koennen.
Karl schrieb:> Es wird so oder so nicht mehr Speicher.
Der Speicher selbst wird nicht mehr, aber der Speicher kann effizenter
genutzt werden.
Rolf M. schrieb:
...> Beim AVR zumindest muss er nach unten wachsen. Und wenn er am
unteren
> Ende vom RAM angekommen ist, läuft er erst in die I/O-Register, dann in> die ALU-Register.
...
grins Da fällt mir ein alter Hack wieder ein: In der Steinzeit der
AVRs haben wir mal für einen Buffer genau soviel SRAM gebraucht, wie der
AVR an Bord hatte. In dem Fall war es praktisch, dass man den Stack in
die Registerbank legen kann...
Unbenutzte IO/Peripherieregister sind auch für einzelne Bits, manchmal
ganze Bytes, gut...
Dadurch dass der AVR den Stack im RAM-Speicher hat, gibt es plötzlich
auch die Möglichkeit von dynamisch berechneten Rücksprungadressen. Ein
echter Fortschritt gegenüber den PICs.
Marcus H. schrieb:> grins Da fällt mir ein alter Hack wieder ein: In der Steinzeit der> AVRs haben wir mal für einen Buffer genau soviel SRAM gebraucht, wie der> AVR an Bord hatte. In dem Fall war es praktisch, dass man den Stack in> die Registerbank legen kann...
Welcher AVR soll denn das gewesen sein?
Ich hatte das auch mal probiert bei einem ATTiny25, R0..R15 als 16 Bytes
Stack zu benutzen. Nur geht das nicht, Push/Pop, Call/Ret gehen nur im
SRAM-Bereich.
Peter D. schrieb:> Marcus H. schrieb:>> grins Da fällt mir ein alter Hack wieder ein: In der Steinzeit der>> AVRs haben wir mal für einen Buffer genau soviel SRAM gebraucht, wie der>> AVR an Bord hatte. In dem Fall war es praktisch, dass man den Stack in>> die Registerbank legen kann...>> Welcher AVR soll denn das gewesen sein?> Ich hatte das auch mal probiert bei einem ATTiny25, R0..R15 als 16 Bytes> Stack zu benutzen. Nur geht das nicht, Push/Pop, Call/Ret gehen nur im> SRAM-Bereich.
Aaalt, erster AVR Kern. 2313, 8515, ... die Liga. Als alle Peripherie
noch direkt an der MCU gehangen ist und nicht, wie bei neueren
Bausteinen über Trampolin (sprich indirekt), angesprochen werden musste.
Versteh mich nicht falsch - Microchip hat den Bausteinen ein nettes
Facelift verpasst, so dass man sie Very-Low-Power Controller neben einen
STM32 hängen kann. Aber die Klarheit aus der Anfangszeit ist weg.
Kaj schrieb:> Ja. Ueberleg doch mal
Danke fürs Bild, die Frage war aber rethorisch gemeint.
Tatsächlich habe ich viel überlegt und auch schon diverse Probleme damit
gehabt. Entweder bin ich zu doof oder Ihr ;-) Bitte nicht falsch
verstehen, aber ich kann einfach keinen echten Vorteil erkennen, wenn
man Stack und Heap unkontrolliert aufeinander zu wachsen lässt. Das
wird aber in diversen Implementierungen genau so gemacht und hat auch in
der Praxis schon viel Mist verursacht.
In dem Moment wo ich zumindest den Heap kontrolliere muss ich mir aber
schon Gedanken gemacht haben. Dann ist der Weg zu einer IMO besseren
Variante (entsprechender Controller vorausgesetzt) nicht mehr weit.
Sobald ein RTOS im Spiel ist, muss man sich meistens Gedanken machen,
welcher Task/Prozess/Thread wieviel Stack braucht. Das ins Linkerskript
zu klopfen juckt mich auch nicht mehr.
Kaj schrieb:> Der Speicher selbst wird nicht mehr, aber der Speicher kann effizenter> genutzt werden.
Für die "maximale" Nutzung müsste man schon auf's Byte genau wissen
wieviel Stack und Heap man belegen wird. Viel Spaß dabei, aber nicht
wundern, wenn es manchmal nicht so läuft wie geplant.
Was meinst Du mit "effizient"?
Woran erkennst Du, dass Dir gerade der Speicher ausgegangen ist?
Meine Meinung habe ich. Zumindest ein valides Argument dafür auch. Was
sind die Argumente dagegen (und damit pro "Heap -> <- Stack")? Ich habe
nichts dagegen meinen Horizont zu erweitern.
Karl schrieb:> Meine Meinung habe ich. Zumindest ein valides Argument dafür auch. Was> sind die Argumente dagegen (und damit pro "Heap -> <- Stack")? Ich habe> nichts dagegen meinen Horizont zu erweitern.
Na im Grenzfall kann Heap oder eben der Stack den maximalen Speicher
nutzen, im anderen Szenario kann es sein daß der Heap überläuft obwohl
der Stack fast leer ist (oder andersrum)
In einem der Fälle ist Heap und Stack jeweils maximal 1/2 des Speichers,
im anderen Fall kann der Speicher dynamisch zwischen Heap und Stack
aufgeteilt werden.