Laut diesem Film (ca. Minute 38) verwenden zwei Funktionen mit denselben lokalen Variablen denselben Speicherplatz. Also sollte das folgende Programm drucken 5
. Kompilieren mit gcc
Ergebnissen -1218960859
. Warum?
Das Programm:
#include <stdio.h>
void A()
{
int a;
printf("%i",a);
}
void B()
{
int a;
a = 5;
}
int main()
{
B();
A();
return 0;
}
Wie angefordert, ist hier die Ausgabe vom Disassembler:
0804840c <A>:
804840c: 55 push ebp
804840d: 89 e5 mov ebp,esp
804840f: 83 ec 28 sub esp,0x28
8048412: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
8048415: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
8048419: c7 04 24 e8 84 04 08 mov DWORD PTR [esp],0x80484e8
8048420: e8 cb fe ff ff call 80482f0 <printf@plt>
8048425: c9 leave
8048426: c3 ret
08048427 <B>:
8048427: 55 push ebp
8048428: 89 e5 mov ebp,esp
804842a: 83 ec 10 sub esp,0x10
804842d: c7 45 fc 05 00 00 00 mov DWORD PTR [ebp-0x4],0x5
8048434: c9 leave
8048435: c3 ret
08048436 <main>:
8048436: 55 push ebp
8048437: 89 e5 mov ebp,esp
8048439: 83 e4 f0 and esp,0xfffffff0
804843c: e8 e6 ff ff ff call 8048427 <B>
8048441: e8 c6 ff ff ff call 804840c <A>
8048446: b8 00 00 00 00 mov eax,0x0
804844b: c9 leave
804844c: c3 ret
804844d: 66 90 xchg ax,ax
804844f: 90 nop
c
gcc
memory-management
elyashiv
quelle
quelle
Antworten:
Ja, ja, dies ist ein undefiniertes Verhalten , da Sie die Variable uninitialized 1 verwenden .
Auf der x86-Architektur 2 sollte dieses Experiment jedoch funktionieren . Der Wert wird nicht vom Stapel "gelöscht", und da er nicht initialisiert
B()
ist, sollte derselbe Wert immer noch vorhanden sein, vorausgesetzt, die Stapelrahmen sind identisch.Ich wage das zu erraten, da
int a
nicht wird verwendet , innerhalb vonvoid B()
, den Compiler optimiert , dass Code, und ein 5 wurde nie auf dem Stapel an diese Stelle geschrieben. Versuchen Sie auch, einprintf
In hinzuzufügenB()
- es könnte einfach funktionieren.Auch Compiler-Flags - nämlich die Optimierungsstufe - werden wahrscheinlich auch dieses Experiment beeinflussen. Versuchen Sie, Optimierungen zu deaktivieren, indem Sie
-O0
an gcc übergeben.Bearbeiten: Ich habe gerade Ihren Code mit
gcc -O0
(64-Bit) kompiliert , und tatsächlich druckt das Programm 5, wie man es von dem mit dem Aufrufstapel vertrauten erwarten würde. In der Tat funktionierte es auch ohne-O0
. Ein 32-Bit-Build kann sich anders verhalten.Haftungsausschluss: Verwenden Sie niemals so etwas in "echtem" Code!
1 - Im Folgenden wird diskutiert, ob dies offiziell "UB" oder nur unvorhersehbar ist.
2 - Auch x64 und wahrscheinlich jede andere Architektur, die einen Aufrufstapel verwendet (mindestens eine mit einer MMU)
Schauen wir uns einen Grund an, warum es nicht funktioniert hat. Dies ist am besten in 32 Bit zu sehen, also werde ich mit kompilieren
-m32
.Ich habe kompiliert mit
$ gcc -m32 -O0 test.c
(Optimierungen deaktiviert). Wenn ich das ausführe, wird Müll gedruckt.Betrachten
$ objdump -Mintel -d ./a.out
:Wir sehen, dass
B
der Compiler 0x10 Bytes Stapelspeicher reserviert und unsereint a
Variable auf[ebp-0x4]
5 initialisiert hat .In
A
platzierte sich der Compiler jedochint a
bei[ebp-0xc]
. In diesem Fall sind unsere lokalen Variablen also nicht am selben Ort gelandet! Wenn Sie ebenfalls einenprintf()
Aufruf hinzufügen,A
werden die Stapelrahmen fürA
undB
identisch und gedruckt55
.quelle
Es ist undefiniertes Verhalten . Eine nicht initialisierte lokale Variable hat einen unbestimmten Wert, und ihre Verwendung führt zu undefiniertem Verhalten.
quelle
{ int uninit; &uninit; printf("%d\n", uninit); }
immer noch ein undefiniertes Verhalten auf. Auf der anderen Seite können Sie jedes Objekt als Array von behandelnunsigned char
. hast du das gedachtEine wichtige Sache, an die Sie sich erinnern sollten - verlassen Sie sich niemals auf so etwas und verwenden Sie dies niemals in echtem Code! Es ist nur eine interessante Sache (die sogar nicht immer wahr ist), keine Funktion oder ähnliches. Stellen Sie sich vor, Sie versuchen, einen Fehler zu finden, der durch diese Art von "Feature" - Albtraum - hervorgerufen wird.
Übrigens. - C und C ++ sind voll von solchen "Funktionen". Hier ist eine großartige Diashow dazu: http://www.slideshare.net/olvemaudal/deep-c Wenn Sie also ähnliche "Funktionen" sehen möchten, verstehen Sie, was ist unter der Haube und wie es funktioniert, schauen Sie sich einfach diese Diashow an - Sie werden es nicht bereuen und ich bin sicher, dass selbst die meisten erfahrenen C / C ++ - Programmierer viel daraus lernen können.
quelle
In der Funktion
A
wird die Variablea
nicht initialisiert. Das Drucken ihres Werts führt zu undefiniertem Verhalten.In einigen Compilern befinden sich die Variablen
a
inA
unda
inB
an derselben Adresse, sodass sie möglicherweise gedruckt5
werden. Sie können sich jedoch auch hier nicht auf undefiniertes Verhalten verlassen.quelle
s machine will be the same depends on the assembly generated by the compiler. As @JonathonReinhart pointed out the call to
B () `möglicherweise optimiert wurden.5
, aber anscheinend hat Jonathon Reinhart eine viel bessere Erklärung.Kompilieren Sie Ihren Code mit
gcc -Wall filename.c
Sie werden diese Warnungen sehen.In c Das Drucken einer nicht initialisierten Variablen führt zu einem undefinierten Verhalten.
In Abschnitt 6.7.8 Initialisierung des C99-Standards heißt es
Edit1
As @Jonathon Reinhart Wenn Sie die Optimierung mit dem
-O
Flag Using deaktivieren, erhaltengcc-O0
Sie möglicherweise Ausgabe 5.Dies ist jedoch überhaupt keine gute Idee. Verwenden Sie dies niemals im Produktionscode.
-Wuninitialized
Dies ist eine der wertvollen Warnungen, die Sie in Betracht ziehen sollten. Sie sollten diese Warnung, die zu großen Schäden in der Produktion führt, wie z. B. Abstürze beim Ausführen von Dämonen, weder deaktivieren noch überspringen.Edit2
Deep C- Folien erklärt Warum das Ergebnis 5 / Garbage ist. Hinzufügen dieser Informationen aus diesen Folien mit geringfügigen Änderungen, um diese Antwort etwas effektiver zu machen.
Möglicherweise verfügt dieser Compiler über einen Pool benannter Variablen, die er wiederverwendet. ZB wurde die Variable a verwendet und freigegeben.
B()
WennA()
ein ganzzahliger Name benötigta
wird, erhält die Variable denselben Speicherort. Wenn Sie die Variable umbenennen inB()
, sagen wirb
, dann glaube ich nicht , erhalten Sie5
.Wenn der Optimierer aktiviert wird, kann eine Menge passieren. In diesem Fall würde ich vermuten, dass der Aufruf von
B()
übersprungen werden kann, da er keine Nebenwirkungen hat. Auch wäre ich nicht überrascht, wenn dasA()
eingefügt istmain()
, dh kein Funktionsaufruf. (DaA ()
der Linker jedoch sichtbar ist, muss der Objektcode für die Funktion dennoch erstellt werden, falls eine andere Objektdatei mit der Funktion verknüpft werden soll.) Ich vermute jedenfalls, dass der gedruckte Wert etwas anderes ist, wenn Sie den Code optimieren.Müll!
quelle