guten morgen,
ich habe eine Funktion, mit einer for-schleifen
1
funktion(conststructdataStruct*data_ps)
2
{
3
for(uint8_tx=0;x<data_ps->length;x++)
4
{
5
uint8_ty=getAddress(data_ps->data[x]);
6
if(y<NOF_END)
7
{
8
.........
9
}
10
}
11
/* y ist hier nicht mehr gültig bzw. vorhanden */
12
.........
13
}
die Variable y ist ja jetzt nur innerhalb der for-schleife gültig.
Möchte ich diese außerhalb auch noch benutzen, muss ich diese entweder
neu oder außerhalb der schleife definieren.
Meine Frage ist bezüglich des speicherplatzes.
Ist dieser unterschiedlich?
1
funktion(conststructdataStruct*data_ps)
2
{
3
uint8_ty;
4
for(uint8_tx=0;x<data_ps->length;x++)
5
{
6
y=getAddress(data_ps->data[x]);
7
if(y<NOF_END)
8
{
9
.........
10
}
11
}
12
/* y ist hier gültig und vorhanden */
13
.........
14
}
oder ist das letztendlich das selbe?
Aber wie ist das dann im gebauten code? Also woran liegt es, dass diese
nicht mehr gültig ist?
Die Variable liegt in beiden Fällen im Stapelspeicher. Dieser ist bis
zum Ende der Funktion gültig.
Bei der Gültigkeit geht es darum, welche Teile deines Quelltextes über
den namen der Variable auf sie zugreifen können. Das ist eher eine
Einschränkung der Programmiersprache, ganz unabhängig von den Vorgängen
in der Hardware.
Jetzt kommt ein große ABER:
Der Compiler hat an dieser Stelle die Freiheit, ein CPU Register anstatt
einer Variable im RAM zu benutzen. Und er hat auch die Freiheit, das
Register am Ende des gültigen Bereiches für andere Zwecke zu verwenden.
Du kannst davon ausgehen, dass er das auch tun wird.
Fabian schrieb:> /* y ist hier nicht mehr gültig bzw. vorhanden */
An dieser Stelle sind weder x noch y vorhanden.
Du mußt doch nur die {} Pärchen verfolgen, Dein Editor sollte die auch
anzeigen. Was innerhalb eines {} definiert ist, bleibt auch innerhalb.
Darf der Compiler hier den Speicherplatz von y (auf dem Stack) nach der
For-Schleife für z recyclen, so dass beide Variablen im RAM die gleiche
Adresse haben?
Steve van de Grens schrieb:> Darf der Compiler hier den Speicherplatz von y (auf dem Stack) nach der> For-Schleife für z recyclen, so dass beide Variablen im RAM die gleiche> Adresse haben?
ja
Steve van de Grens schrieb:> Die Variable liegt in beiden Fällen im Stapelspeicher. Dieser ist bis> zum Ende der Funktion gültig.
Sicher? Wo ist dieses Verhalten definiert?
Mal das hier angenommen:
1
funktion(conststructdataStruct*data_ps)
2
{
3
for(uint8_tx=0;x<data_ps->length;x++)
4
{
5
uint8_ty=getAddress(data_ps->data[x]);// y liegt auf dem Stack
6
if(y<NOF_END)
7
{
8
.........
9
}
10
}
11
/* y ist hier formell nicht mehr gültig bzw. vorhanden */
12
// Frage: ist y hier immer noch auf den Stack?
13
14
...guttausendZeilenCode...
15
uint8_tu=...
16
...miteinigenDaklarationendazwischen...
17
uint8_tv=...
18
...nochmaleinpaarhundertZeilenCode...
19
20
// Und hier: ist der alte Wert y hier immer noch auf dem Stack?
21
// Oder wird der Speicherplatz von nachfolgend deklarierten Variablen verwendet?
Steve van de Grens schrieb:> Darf der Compiler hier den Speicherplatz von y (auf dem Stack) nach der> For-Schleife für z recyclen
Ja. Selbst dann wenn das y außerhalb einer Schleife deklariert ist, aber
danach nicht mehr verwendet wird. Wie schon erwähnt werden lokale
Variablen oft nur in Registern gehalten, und die werden ständig
"recycelt". Es sei denn, man schreibt irgendwo &y , dann muss die
Variable auf den Stack und der Speicher muss verfügbar bleiben bis zum
Ende des Scope.
Lothar M. schrieb:>> Die Variable liegt in beiden Fällen im Stapelspeicher. Dieser ist bis>> zum Ende der Funktion gültig.> Sicher? Wo ist dieses Verhalten definiert?
Das habe ich so in der Ausbildung gelernt. Bei Eintritt in die Funktion
wird Stack belegt und bei Austritt wir der Stack wieder frei gegeben.
Dein Beispiel unter dem Einwand deckt sich mit meiner danach folgenden
Frage, die in Beitrag "variable innerhalb oder außerhalb einer for-schleife unterschied der Gültigkeit"
beantwortet wurde. Das hat sich wohl zeitlich überschnitten.
> Und hier: ist der alte Wert y hier immer noch auf dem Stack?
Wahrscheinlich nicht. Der Stack an sich ist aber bis zum Ende der
Funktion gültig.
Programmierer schrieb:> Es sei denn, man schreibt irgendwo &y , dann muss die> Variable auf den Stack und der Speicher muss verfügbar> bleiben bis zum Ende des Scope.
Wirklich? Kann man sich darauf verlassen, dass der Speicherplatz in
diesem Fall nicht recycelt wird?
Lothar M. schrieb:> Ist dir der Begriff "Scope" oder Sichtbarkeit" geläufig?
Ja. Die Regeln der Sprache sagen aber nur wenig darüber aus, was
wirklich im Speicher passiert (wohl mit Absicht).
Die Frage war ja:
Fabian schrieb:> Meine Frage ist bezüglich des Speicherplatzes. Ist dieser unterschiedlich?
Steve van de Grens schrieb:> Das habe ich so in der Ausbildung gelernt. Bei Eintritt in die Funktion> wird Stack belegt und bei Austritt wir der Stack wieder frei gegeben.
Das ist lediglich ein für reale Compiler typisches Verhalten, keine
Spracheigenschaft.
Steve van de Grens schrieb:> Die Frage war ja:>> Fabian schrieb:>> Meine Frage ist bezüglich des Speicherplatzes. Ist dieser unterschiedlich?
Nein. Bei eingeschalteter Optimierung macht der Compiler daraus ziemlich
sicher das gleiche. Und wie weiter oben ja schon angemerkt wurde, werden
die Variablen der Schleife alle in Registern gehalten.
Oliver
Lothar M. schrieb:> // Und hier: ist der alte Wert y hier immer noch auf dem Stack?> // Oder wird der Speicherplatz von nachfolgend deklarierten Variablen> verwendet?> }
Die allgemein gültige Antwort: "das kommt drauf an" (was das Compiler
draus macht). Höchstwahrscheinlich ist es so (der Speicherplatz wird von
nachfolgenden Variablen verwendet), aber garantieren kann man es nicht.
Wenn y jetzt ein hochgeheimer Wert wäre (z.B. ein privater Key zur
Verschlüsselung), dann sollte man tunlichst andere Maßnahmen ergreifen,
um diesen sicher aus dem Speicher zu entfernen als eine andere Variable
anzulegen, und zu hoffen, daß der Compiler damit den hochgeheimen Wert
überschreibt.
Wenn man es wirklich wissen möchte was passiert, dann wird einem ein
Blick in das Assembler-File nicht erspart bleiben. Aber Vorsicht: Wenn
sich der Code Ändert, oder man an den Compiler-Optimierungen
rumschraubt, kann das Ergebnis ganz anders sein.
Gruß
Steve van de Grens schrieb:> Programmierer schrieb:>>> Es sei denn, man schreibt irgendwo &y , dann muss die>> Variable auf den Stack und der Speicher muss verfügbar>> bleiben bis zum Ende des Scope.>> Wirklich? Kann man sich darauf verlassen, dass der Speicherplatz in> diesem Fall nicht recycelt wird?
Ja, der Sprachstandard verlangt das. Bei so einem Code:
1
intfoo(){
2
inty=42;
3
int*p=&y;
4
5
// ... ganz viel Code mit vielen Variablen der aber y und p nicht benutzt ...
6
7
return*p;
8
}
Muss 42 zurückgegeben werden, y muss bis zum Ende existieren. Wenn man
aber nirgendwo einen Zeiger (oder in C++: eine Referenz) auf y nimmt,
sieht es anders aus.
Am Besten ist es sich den Assembler-Code anzuschauen, das ist sehr
lehrreich.
Programmierer schrieb:> Muss 42 zurückgegeben werden, y muss bis zum Ende existieren.
Muß nicht, der Compiler darf zu "return 42;" optimieren.
Compiler sind typisch recht faul. Sie legen Variablen erst zum spätest
möglichen Zeitpunkt an.
"int y = 42;" bedeutet also nicht, daß y auch sofort angelegt wird.
Programmierer schrieb:>> Wirklich? Kann man sich darauf verlassen, dass der Speicherplatz in>> diesem Fall nicht recycelt wird?>> Ja, der Sprachstandard verlangt das. Bei so einem Code:
Entscheidend ist die "as if" Regel: Der Compiler muss sich so verhalten,
dass das Ergebnis im Rahmen des Sprachstandards stimmt.
> Muss 42 zurückgegeben werden, y muss bis zum Ende existieren.
Bei deinem Code kann ein Compiler auch einfach auf { ... return 42; }
entscheiden, wenn er erkennt, dass in dem "ganz vielen Code" nichts über
y und p vorkommt.
1
voidg(void);
2
intf(void)
3
{
4
intx=0;
5
int*p=&x;
6
g();
7
return*p;
8
}
kann z.B. diesen Code ergeben:
1
f:
2
subq $8, %rsp
3
call g
4
xorl %eax, %eax
5
addq $8, %rsp
6
ret
Man sieht, dass der Speicherplatz für x zwar existiert, aber in keiner
Weise genutzt wird. Für p existiert überhaupt kein Speicherplatz, weil
nicht genutzt und deshalb komplett wegoptimiert.
Programmierer schrieb:> int foo () {> int y = 42;> int* p = &y;> ...> return *p;> }
Aber dann zeigt der Return Wert doch in den Stack der Funktion. Der
Stack ist außerhalb der Funktion nicht mehr gültig, also kann der
Aufrufer mit dem Zeiger nichts zuverlässiges mehr anfangen.
Ich denke, wenn man einen Zeiger auf etwas zurück liefert, dann muss das
im Heap liegen (also innerhalb der Funktion mit new oder malloc()
erzeugt werden).
Steve van de Grens schrieb:> Aber dann zeigt der Return Wert doch in den Stack der Funktion.
Nein. Es wird ja nicht der Pointer p zurück gegeben, sondern das, worauf
er zeigt.
der_eine schrieb:> Wenn y jetzt ein hochgeheimer Wert wäre (z.B. ein privater> Key zur Verschlüsselung), dann sollte man tunlichst andere> Maßnahmen ergreifen, um diesen sicher aus dem Speicher zu> entfernen als eine andere Variable anzulegen, und zu hoffen,> daß der Compiler damit den hochgeheimen Wert überschreibt.
Ach ja, einfach y einen neuen Wert zuweisen wird i.A. auch nicht
ausreichend sein; denn wenn für den Compiler erkennbar ist, daß y in
weiterer Folge nicht mehr benötigt wird, darf er auch gleich die
Zuweisung wegoptimieren.