Frage zu Reihenfolge der Speicherallokierung
-
Hi,
ich mache gerade meine ersten Schritte mit gcc (Ubuntu) und Pointern. Ich habe ein seltsames Verhalten entdeckt. Am besten mal ein bisschen Code.
#include <stdio.h> int* f() { int i = 5; return &i; } void g() { int j = 25; } int main() { int* x = f(); g(); printf("x = %d\n", *x); return 0; }
Mir ist bewusst, dass ich einen Pointer auf eine lokale Variable in f() zurückgebe. Genau darum geht es in meiner Frage auch.
Führe ich das Programm aus, erhalte ich x = 25 als Ausgabe, was ich mir dadurch erklärt habe, dass in f() ein int erzeugt wird und dessen Adresse zurückgegeben wird. Da der int jedoch lokal war, wird er beim Aufruf von g "überschrieben". Die Adresse, die in main() also in x steht, zeigt später auf die Variable j, die in g() erzeugt wird.
Ist das soweit richtig?Ändere ich jedoch g() so ab:
void g() { int j = 25; int k = 99; }
Dann erhalte ich beim Ausführen des Programms x = 99 als Ausgabe. Das verstehe ich nicht so ganz. Warum kommt hier nicht wieder 25 heraus?
Kann es sein, dass zuerst der Speicher für die Variablen in g() reserviert wird und dann für die lokale Variable in f()? Obwohl die Ausführungsreihenfolge anders ist.
Ist das vielleicht sogar Compiler-abhängig?
Ich würde mich über Antworten freuen
Lg Paul Hans Dieter Horst
-
PaulHansDieterHorst schrieb:
ich mache gerade meine ersten Schritte mit gcc (Ubuntu) und Pointern. Ich habe ein seltsames Verhalten entdeckt.
Du hast entdeckt, wie der GCC ohne Optimierungen seine Variablen organisiert.
Dann erhalte ich beim Ausführen des Programms x = 99 als Ausgabe. Das verstehe ich nicht so ganz. Warum kommt hier nicht wieder 25 heraus?
Anscheinend ordnet der GCC seine Variablen in umgekehrter Definitionsreihenfolge auf dem Stack an.
Kann es sein, dass zuerst der Speicher für die Variablen in g() reserviert wird und dann für die lokale Variable in f()? Obwohl die Ausführungsreihenfolge anders ist.
Ohne Optimierungen äußerst unwahrscheinlich. Selbst mit Optimierungen wird das nur geschehen, wenn interfunktionale Optimierungen erlaubt und möglich sind.
Ist das vielleicht sogar Compiler-abhängig?
Also zunächst einmal ist das vom C-Standard her natürlich total undefiniert. Es braucht nicht einmal einen Stack geben und selbst wenn es diesen gibt, dann ist die Anordnung von Variablen und ob sie überhaupt auf den Stack gelegt werden völlig undefiniert. Du hast entdeckt, dass der GCC in deiner (und auch meiner) Version und ohne Optimierungen anscheinend nach einem bestimmten Muster vorgeht. Dies ist nicht nur hochgradig compilerabhängig, sondern auch von den Einstellungen des Compilers. Bei mir kommt beispielsweise bei O1 32767, bei O2 und höher 0 heraus.
-
Ok, danke!
Könnte es dann z.b. auf einem anderen Rechner / anderen Compliler auch möglich sein, dass einfach irgendwas zufälliges ausgegeben wird? Etwas, was halt gerade da am Speicher steht?
Eine alte Klausuraufgabe war es nämlich, anzugeben, was dort ausgegeben wird. Wenn das nicht standardisiert ist, wirds schwer, da was richtiges zu schreiben
-
Bei mir kommt übrigens bei beiden Varianten 25 heraus (also auch mit der zweiten Variable). Bei -01 kommt irgendetwas zufälliges raus (GCC) oder 1 (Clang). Bei -O2 und höher kommt wie bei SeppJ 0 raus.
-
Was ist denn O1, O2, usw.? Ich bin neu in C
-
Optimierungslevel der Compiler.
-
PaulHansDieterHorst schrieb:
Könnte es dann z.b. auf einem anderen Rechner / anderen Compliler auch möglich sein, dass einfach irgendwas zufälliges ausgegeben wird? Etwas, was halt gerade da am Speicher steht?
Theoretisch könnte alles passieren, der Rechner könnte deine Katze schwängern. Undefiniert eben. Praktisch wird natürlich das ausgegeben, was da gerade im Speicher steht und das ist in der Regel deterministisch, d.h. du wirst mit dem gleichen Programm auf dem gleichen Rechner immer den gleichen Wert bekommen. Auf jedem modernen System wird der Speicher vollständig genullt bevor dein Programm ihn bekommt. Zu deinem Programm gehört aber in der Regel mehr als das was du selber programmiert hast, sondern auch ein bisschen Code, der die main überhaupt aufruft und der die Objekte der Standardbibliothek initialisiert.
Daher siehst du ohne Optimierungen die 25/99 die dein eigenes Programm an die Stelle geschrieben hat. Mit Optimierungen wird das sinnlose Geschreibsel des Programms wegoptimiert, daher siehst du, was vorher an der Stelle war. Je nach Optimierungsstufe ist das anscheinend was leicht anderes. Man könnte nun noch nachforschen, wie diese werte genau zustande kommen, aber das lohnt wohl nicht.Eine alte Klausuraufgabe war es nämlich, anzugeben, was dort ausgegeben wird. Wenn das nicht standardisiert ist, wirds schwer, da was richtiges zu schreiben
Bei der Klausur geht es vermutlich gerade darum, dass man auch solche technischen Hintergründe versteht. Theoretisch musst du, wie schon erklärt, auf deine Katze aufpassen.
-
Ok, alles klar! Vielen Dank