Ist es richtig, dass alles was in einer geschweiften Klammer als
Variable definiert ist, nicht als static declariert und nicht dynamisch
angelegt ist, auf dem Stack landet, daneben noch Ein- und
Rücksprungadressen der Funktionen.
Wie seht ihr das konstruierte unten stehende Beispiel.
Habe ich das soweit richtig verstanden oder passt manches nicht?
Entscheidet der Compiler mit?
Er könnte ja wissen, was alles auf den Stack kommt.
Wenn er das weiß, und die festgelegte Stackgröße kennt, gibt er eine
Fehlermeldung aus, wenn diese beim compilieren überschritten wird?
Gibt es eine Faustformel für die Stackgröße?
Beispiel, wie ich das verstanden habe für Programmiersprache C und C++:
1
intarray_A[100];// Das landet nicht auf dem Stack.
2
// Static kommt wohl von Compiler hinzu.
3
4
staticarray_B[100];// Das landet nicht auf dem Stack.
5
6
intmain(void)
7
{
8
9
// Das landet vermutlich auf dem Stack. Bin mir aber nicht sicher.
10
intarray_C[100];
11
12
// Das landet vermutlich "nicht" auf dem Stack wegen static.
13
staticintarray_D[100];
14
15
for(;;)// Endlosschleife. Wird vom Programm nicht verlassen
16
{
17
18
// z Wird wohl permanent auf den Stack rauf und runtergenommen.
19
intz=2;
20
21
// Die unterschiedlichen Übergabemöglichkeiten sind nur Spielerei.
22
// Sollten aber wohl zulässig sein.
23
// Es wird nur die Referenz auf die Arrays übergeben.
martin schrieb:> Entscheidet der Compiler mit?
Ja, aber nur auf exotischen Plattformen wie 8051, wo der Stack extrem
klein ist. Dort kann er Sachen ins (X-)Data Segent packen - braucht
dafür aber eine Analyse des Programms (Stichwort: Data Overlay).
martin schrieb:> gibt er eine> Fehlermeldung aus, wenn diese beim compilieren überschritten wird?
In aller Regel nicht. Nitpick: Außerdem weiss der Compiler nicht wie
groß der Speicher ist, sondern nur der Linker.
martin schrieb:> Gibt es eine Faustformel für die Stackgröße?
Größe der lokalen Variablen + Rücksprung Addresse, zuzüglich
Unterfunktionen. Oh, und auf einem µC mit verschachtelten Interrupts
kommen die natürlich noch dazu.
martin schrieb:> Wenn er das weiß, und die festgelegte Stackgröße kennt, gibt er eine> Fehlermeldung aus, wenn diese beim compilieren überschritten wird?
-Wstack-usage=...
martin schrieb:> Ist es richtig, dass alles was in einer geschweiften Klammer als> Variable definiert ist, nicht als static declariert und nicht dynamisch> angelegt ist, auf dem Stack landet, daneben noch Ein- und> Rücksprungadressen der Funktionen.
Es ist für andere viel leichter, deine Ergüsse zu lesen, wenn du Fragen
auch mit einem Fragezeichen beendest. Und nein, das ist nicht (komplett)
richtig. Aber auch nicht komplett falsch.
Beispielsweise kommt die Einsprungadresse einer Funktion nie auf den
Stack (wozu auch?). Und Rücksprungadresse und Daten müssen nicht auf dem
selben Stack liegen. Viele Architekturen bieten mehrere Stacks. Andere
haben nur einen sehr beschränkten Returnstack, weswegen der Compiler
einen Datenstack in Software implementiert. Details hängen extrem von
der Zielarchitektur ab.
> Entscheidet der Compiler mit?
Immer.
> Wenn er das weiß, und die festgelegte Stackgröße kennt, gibt er eine> Fehlermeldung aus, wenn diese beim compilieren überschritten wird?
Der Compiler weiß für gewöhnlich nicht, wie groß der Stack einmal sein
wird.
> Gibt es eine Faustformel für die Stackgröße?
Nein. Denn die hängt bei nichttrivialen Programmen meist auch von den
Eingabedaten ab. Einfachstes Beispiel: eine rekursive Funktion, z.B. für
die Berecnnung der Fibonacci-Zahlen.
Statische Ermittlung der Stackgröße ist nur möglich, wenn man auf
(legale) Sprachmittel wie z.B. Rekursion verzichtet.
> int array_A[100]; // Das landet nicht auf dem Stack.> // Static kommt wohl von Compiler hinzu.
Hier kommt natürlich kein static hinzu.
> static array_B[100]; // Das landet nicht auf dem Stack.
Bei globalen Variablen ist static bedeutungslos.
> int main(void)> {>> // Das landet vermutlich auf dem Stack. Bin mir aber nicht sicher.> int array_C[100];
Hier kann der Compiler frei entscheiden.
> // Das landet vermutlich "nicht" auf dem Stack wegen static.> static int array_D[100];static Variablen genauso wie globale Variablen können nicht auf dem
Stack liegen, weil ihre Lebenszeit das gesamte Programm ist. Stackframes
haben aber immer eine beschränkte Lebenszeit - vom Eintritt in den bis
zum Verlassen des zugehörigen Blocks.
> for(;;) // Endlosschleife. Wird vom Programm nicht verlassen> {>> // z Wird wohl permanent auf den Stack rauf und runtergenommen.> int z = 2;
Nicht notwendigerweise. Könnte genauso gut permanent in einem Register
gehalten werden. Der Compiler entscheidet.
> void funktion(int a[], int b[], int *p_c, int d[], int y)> {> int array_E[100]; // Kommt auf den Stack und bei verlassen der> // Funktion wieder runter.
nicht notwendigerweise, s.o.
> static int array_F[100]; // Ist nicht auf dem Stack.
Korrekt. s.o.
> // a, b, p_c, d, y kommen auf den Stack
Oder sind permanent in Registern. s.o.
Tja, kommt wohl auf die Zielarchitektur an...
Klar kann man sich da 3, oder auch 27 Stacks bereitstellen
lassen.
Fragt sich nur, ob das verfügbare RAM genug Platz bietet:
Bei manchem AVR-Tiny sind nur 128 Byte für arrays,
Zwischenspeicher für Register und Rücksprungadressen
vorhanden. Auch bei den kleinen AVR-Mega sind es nur
wenige Kbyte...
Also - was bietet der µC deiner Wahl an RAM???
martin schrieb:> Ist es richtig, dass alles was in einer geschweiften Klammer als> Variable definiert ist, nicht als static declariert und nicht dynamisch> angelegt ist, auf dem Stack landet
Nein, der Compiler kann auch beschließen, dass er solche Variablen in
Register packt.
Axel S. schrieb:>> int array_A[100]; // Das landet nicht auf dem Stack.>> // Static kommt wohl von Compiler hinzu.>> Hier kommt natürlich kein static hinzu.>>> static array_B[100]; // Das landet nicht auf dem Stack.>> Bei globalen Variablen ist static bedeutungslos.
Da muss ich dir teilweise wiedersprechen. Static ist bei globalen
Variablen nicht bedeutungslos, hat aber eine ganz andere Bedeutung als
in Funktionen.
Bei globalen Variablen und Funktionen hat static Einfluss auf die
Sichtbarkeit beim linken. Static bewirkt eine lokale Sichtbarkeit,
also innerhalb des Moduls bzw. der Datei und nicht in anderen Dateien.
Siehe:
https://stackoverflow.com/questions/4239834/global-variable-in-c-are-static-or-not
compilerflags setzen:
-fstack-usage -Wstack-usage=512
wobei 512 eine stackgröße ist
das ergibt eine Warnung bei übercshreiten der 512ytes
wenn du nur 512byte stack hast , setzt du vieleicht lieber 400bytes an
dann bleibt auch für ISR und so genug luft
fstack-usage erzeugt beim compilieren zusätzliche dateien
wenn man nun das durchsucht iwrd für jede funktion ene stackgröße
angegeben
auch für ISRs und so
so findet an ggf stackfresser
Omega G. schrieb:> Axel S. schrieb:>>> int array_A[100]; // Das landet nicht auf dem Stack.>>> // Static kommt wohl von Compiler hinzu.>>>> Hier kommt natürlich kein static hinzu.>>>>> static array_B[100]; // Das landet nicht auf dem Stack.>>>> Bei globalen Variablen ist static bedeutungslos.>> Da muss ich dir teilweise wiedersprechen. Static ist bei globalen> Variablen nicht bedeutungslos, hat aber eine ganz andere Bedeutung als> in Funktionen.
Stimmt. Ich war da in Gedanken immer noch beim Einfluß der Attribute auf
die Art, wie der Compiler die Variable anlegt. Darauf hat das static
keinen Einfluß, wohl aber auf die Sichtbarkeit.
martin schrieb:> Ist es richtig, dass alles was in einer geschweiften Klammer
Gemeint ist der Body einer Funktion?
> als Variable definiert ist, nicht als static declariert und nicht> dynamisch angelegt ist, auf dem Stack landet, daneben noch Ein- und> Rücksprungadressen der Funktionen.
Im C-Standard gibt es keinen "Stack"; wie das umgesetzt wird, hängt vom
Compiler / ABI ab. Im einfachsten Falle würde ein Compiler alles auf
den Stack klatschen; bis auf zu übergebende Argumente von Funktionen —
da schreibt das ABI vor wie und wo die zu übergeben sind.
> int main(void)> {> // Das landet vermutlich auf dem Stack. Bin mir aber nicht sicher.> int array_C[100];
Prinzipiell ja. Falls der Compiler Details von "funktion" kennt (wenn
"funktion" z.B. nur das erste Element braucht / verändert) ist
vorstellbar, dass kein Array angelegt wird sondern nur das erste Element
in einem Prozessorregister.
> // Das landet vermutlich "nicht" auf dem Stack wegen static.> static int array_D[100];
Wird im Static Sorage angelegt. Aber wie immer gilt die "as-if" Regel:
Die vom Compiler generierte Code muss sich verhalten wie die vom
Standard beschriebene abstrakte Maschine. Wie ein Compiler das konkret
erreicht ist seine Sache, wobei natürlich Standard und ABI einzuhaltende
Rahmenbedingungen sind.
> for(;;) // Endlosschleife. Wird vom Programm nicht verlassen> {> // z Wird wohl permanent auf den Stack rauf und runtergenommen.> int z = 2;
Hängt auch von der C(++) Implementation ab. Der Wert ist simpel genug,
einfach eine "2" in das Register zu laden, in dem das 5. Argument von
"funktion" übergeben wird. Falls dieses auf dem Stack zu übergeben ist,
wird der Wert "2" auf den Stack gelegt, aber dafür brauch "z" nicht im
Aufrufer auf dem Stack zu liegen.
Ein ABI könnte sogar festlegen, dass die Adresse von z übergeben wird,
was dann dem Handling von großen Strukturen ähneln würde. Der Calle
würde über die Adresse auf z zugreifen, und wenn z im Calle verändert
würde, lokal eine Kopie von z ziehen.
Wie / wann der Stack manipuliert wird ist Detail der Implementation und
lässt sich anhand von Quellcode, Compiler und Architektur lediglich
erahnen.
Falls in der Schleife mehr Stack gebraucht wird, muss der nicht
unbedingt in jedem Durchlauf angepasst werden. Evtl. kann die Anpassung
auch aus der Schleife rausgezogen werden bis an den Anfang der Funktion,
wo die Anpassung dann einmal im Prolog erfolgt und falls nötig in den
Epilogen entsprechend rückabgewickelt wird. Da main nie verlassen wird,
braucht es keine Epiloge.
> // Die unterschiedlichen Übergabemöglichkeiten sind nur Spielerei.> // Sollten aber wohl zulässig sein.> // Es wird nur die Referenz auf die Arrays übergeben.> // z wird kopiert> funktion(array_A, array_B, &array_C[0], &array_D[0], z);>> [...]> // a, b, p_c, d, y kommen auf den Stack> // Beim Verlassen der Funktion werden sie vom Stack genommen.
Wo und wie sie übergeben werden ist wie gesagt Sache des ABI. Und wer
für's Aufräumen des Stack verantwortlich ist kann auch von der
Implementation abgängen: Caller oder Callee.
Es kann z.B. sein, dass der Callee nicht weiß, wie viel Stack der caller
belegt hat, und kann ihn deshalb auch nicht freigeben. Das ist z.B. mit
varargs der Fall.
Auch die Übergabe von Strukturen wird im ABI geregelt. Kleine werden
i.d.R. kopiert und in Registern übergeben, große oder solche mit Größe 0
werden auf dem Stack angelegt und deren Adresse übergeben. Wird eine
Struktur zurückgegeben, dann in Registern oder der Caller übergibt die
Adresse des Bereicht als zusätzliches, implizites Adressargument.
Strukturen per Wert zu übergeben erfordert manchmal das Kopieren der
Struktur, und auch hier bestimmt die Implementation oder das ABI, wer
kopiert: Caller oder Callee. Das Kopieren dem Callee zu überlassen hat
den Vorteil, dass, wenn keine Kopie notwendig ist (z.B. wenn nur lesend
auf die Struktur zugegriffen wird) das Kopieren wegoptimiert werden
kann.
Am besten nimmst du dir einen Compiler für deine bevorzugte Architektur
und schaust an, wie der kleine Beispiele übersetzt und wo er was wie
anlegt und übergibt. Den erzeugten Code zu studieren bringt da oft mehr
Klarheit als mit einem Debugger oder Simulator durchzunudeln.
Hallo,
ich kleb mich mal hier drunter. Habe mir das alles durchgelesen aber
wirklich schlauer bin ich auch nicht. Ich habe das gleiche Thema für den
STM32F103 mit dem GCC. Wie gross den Stack? Ok, Programm läuft aber die
0x100 standen da auch schon vorher drin.
Wi erzeugt der Compiler zb ein 10.000 Elemente Array was ich lokal in
einer Routine habe? Das geht durchaus. 18,5 kb habe ich noch frei,.
Die einzige Ausgabe, die ich kriege ist die hier von EmBitz:
bin\Release\f103.map|1|Program size (bytes): 42076|
||Data size (bytes): 248|
||BSS size (bytes): 1664|
|| ----------------|
||Total size (bytes): 43988 (R/W Memory: 1912)|
|||
||=== Build finished: 0 errors, 0 warnings (0 minutes, 4 seconds) ===|
Christian J. schrieb:> Wi erzeugt der Compiler zb ein 10.000 Elemente Array was ich lokal in> einer Routine habe?
Wenn Du es innerhalb Deiner Funktion deklarierst, und nicht als
static, dann landet es mit sehr hoher Wahrscheinlichkeit auf dem Stack
(auch wenn Johann sehr ausführlich die Möglichkeiten beschrieben hat,
unter denen es das möglicherweise nicht tun wird). Geh' einfach mal
davon aus, daß es das tut; schnapp Dir Deinen Debugger und sieh Dir den
Stackpointer unmittelbar vor Aufruf der Funktion an, steppe in die
Funktion und sieh Dir jetzt den Stackpointer an.
Rufus Τ. F. schrieb:> auch wenn Johann sehr ausführlich die Möglichkeiten beschrieben hat,
Ja, weiss ich. Heisst unterm Strich aber: Es kann alles passieren :-(
Gucken wie ich den Stackpointer reinkriege, liegt im Registersatz
irgendwo "versteckt"
Christian J. schrieb:> Die einzige Ausgabe, die ich kriege ist die hier von EmBitz:
Stackverbrauch steht da nicht drin, aber gcc kann mit -fstack-usage
überredet werden, den Stackverbrauch für jede Funktion anzugeben. Dann
kannste Dir anhand des Callpath ja überlegen, wie groß der
Stackverbrauch ungefähr ist.
Muß man halt noch einen konstanten weiteren Stackverbrauch pro Aufruf
dazuaddieren, das hängt aber von der Architektur ab.
> dann landet es mit sehr hoher Wahrscheinlichkeit auf dem Stack
Bei diesen zusammengestrichenen Beispielen merkt der Compiler, das Array
wird nicht benutzt und wirft es mit sehr hoher Wahrscheinlichkeit
komplett raus. :-)