2013-06-13 3 views
6

Wenn ich C unterrichte, zähle ich manchmal auf GCC, um den "überzeugenden" Teil einiger Regeln zu machen. Zum Beispiel sollte man nicht berücksichtigen, dass eine lokale Variable für eine Funktion den Wert zwischen den Anrufen beibehält.Die lokalen Variablen sind nicht statisch, aber warum bekomme ich dieses Verhalten?

GCC hat mir immer geholfen, diese Lektionen den Schülern beizubringen, indem sie lokalen Variablen den Müll auf den Kopf stellen, damit sie verstehen, was passiert.

Jetzt ist dieses Stück Code definitiv eine harte Zeit.

#include <stdio.h> 

int test(int x) 
{ 
     int y; 
     if(!x) 
       y=0; 
     y++; 
     printf("(y=%d, ", y); 
     return y; 
} 

int main(void) 
{ 
     int a, i; 

     for(i=0; i<5; i++) 
     { 
       a=test(i); 
       printf("a=%d), ", a); 
     } 
     printf("\n"); 
     return 0; 
} 

Die Ausgabe lautet:

(y=1, a=1), (y=2, a=2), (y=3, a=3), (y=4, a=4), (y=5, a=5), 

Aber wenn ich die Zeile Kommentar:

 /* printf("(y=%d, ", y); */ 

Dann ist der Ausgang werden:

a=1), a=32720), a=32721), a=32722), a=32723), 

ich kompilieren den Code mit -Wall wechseln, aber nicht warnen gs beziehen sich auf die Verwendung von lokalen Variablen, ohne sie zu initialisieren.

Gibt es einen GCC-Schalter, der eine Warnung auslöst oder zumindest etwas Müll anzeigt? Ich habe versucht, die Optimierungsschalter, und das half, da der Code Ausgabe wie folgt wurde:

$ gcc test.c -o test -Wall -Os 
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), 
$ gcc test.c -o test -Wall -Ofast 
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), 
$ gcc test.c -o test -Wall -O0 
$ ./test 
(y=1, a=1), (y=2, a=2), (y=3, a=3), (y=4, a=4), (y=5, a=5), 
$ gcc test.c -o test -Wall -O1 
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), 
$ gcc test.c -o test -Wall -O2 
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), 
$ gcc test.c -o test -Wall -O3 
$ ./test 
(y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), (y=1, a=1), 

Aber y = 1 in allen Fällen Art Trick ist. Hat sich der Standard geändert, sodass die lokalen Variablen jetzt mit Nullen initialisiert werden?

+0

Ich glaube nicht, dass C einen Standard für die Initialisierung von Variablen angibt. Tatsächlich denke ich, dass die Erinnerung so bleibt, wie sie ist. Wenn Sie also y hinzufügen, war der Wert von y derselbe wie zuvor und Sie erhöhen ihn dann. – Magn3s1um

+0

Die Übereinstimmung ist, dass der zweite Aufruf der Funktion den gleichen Blockcode des RAM verwendet. –

+1

Ich denke nicht, dass es ein Zufall ist, da der Compiler y zu einer bestimmten Region des Gedächtnisses verbindet, die relativ ist. Es ändert nicht die y-Relation zum ersten Stapel. Wenn also der vorherige Stapel nicht größer wird, ist y immer am selben Platz! – Magn3s1um

Antwort

2

Eine andere mögliche Herangehensweise an diese Idee könnte darin bestehen, zwischen den Aufrufen der test-Funktion eine andere Funktion aufzurufen. Wenn die andere Funktion Stapelspeicherplatz verwendet, werden wahrscheinlich die Stapelwerte geändert. Fügen Sie zum Beispiel vielleicht eine Funktion wie folgt aus:

int changeStack(int x) 
{ 
    int y2 = x + 100; 
    return y2; 
} 

Und dann einen Anruf, um es hinzuzufügen:

for(i=0; i<10; i++) 
    { 
      a=test(i); 
      printf("a=%d), ", a); 
      changeStack(i); 
    } 

Es ist natürlich auf Optimierungsstufen abhängt, aber mit dem Standard-gcc Kompilierung (gcc test.c), ich habe die folgenden Ergebnisse, nachdem es zu ändern, das zu tun:

(y=1, a=1), (y=101, a=101), (y=102, a=102), (y=103, a=103), (y=104, a=104), (y=105, a=105), (y=106, a=106), (y=107, a=107), (y=108, a=108), (y=109, a=109), 
+0

Dieser Ansatz kann gut funktionieren, wenn die GCC alle Speicherinhalte im Speicher speichert, wenn keine Optimierungen angefordert werden. Das Hinzufügen einer "-O" -Flagge wird es fast sicher zum Verschwinden bringen. –

+1

@CarlNorum: Ja, es ist definitiv eine sehr Compiler-spezifische und Optimierung-spezifische Demonstration. Die Verwendung einer Optimierung ändert tatsächlich das Ergebnis. Tricky Business versucht, undefiniertes Verhalten zu demonstrieren. –

+0

Mark, du hast verstanden. Ich denke, kann dieses Beispiel in der Klasse verwenden. Vielen Dank! –

2

Ihr Programm verursacht undefiniertes Verhalten. Wenn Sie einen Wert ungleich null an test() übergeben, wird y nie initialisiert. printf enthalten oder nicht, Sie können sich nicht auf die Ergebnisse verlassen.

Wenn eine Warnung erfolgen soll, Klirren geben Ihnen einen mit -Wsometimes-uninitialized:

example.c:6:12: warning: variable 'y' is used uninitialized whenever 'if' 
     condition is false [-Wsometimes-uninitialized] 
     if(!x) 
      ^~ 
example.c:8:9: note: uninitialized use occurs here 
     y++; 
     ^
example.c:6:9: note: remove the 'if' if its condition is always true 
     if(!x) 
     ^~~~~~ 
example.c:5:14: note: initialize the variable 'y' to silence this warning 
     int y; 
      ^
       = 0 
1 warning generated. 

ich mit ein paar GCC-Versionen ich auf der Hand haben getestet, aber keiner von ihnen würde eine Warnung für mich produzieren.

+0

Ich weiß.Ich möchte diese Vorlesung unter Verwendung von GCC erzwingen (so können die Schüler selbst einen Fehlercode sehen, der von der Kompilation zurückgegeben wird) –

+0

Ich habe gerade die Code-Schleife in 'for (i = 1; i <5; i ++)' geändert und das Ergebnis war: (y = 1, a = 1), (y = 2, a = 2), (y = 3, a = 3), (y = 4, a = 4), Das ist schrecklich in einer Klasse voller Joker. :) –

+0

Ich testete mit gcc-Schalter ('-Waybe-uninitialized') aber keine Warnung. Trotzdem danke. Guter Tipp. –

4

, dass das Problem mit undefiniertem Verhalten ist: es ist „undefined“.

Also ist jeder Satz von Ergebnissen vollständig auf eine Kombination von Compiler/Einstellungen/was ist in Speicher/Interrupts.

Sie können Chance auf einige Einstellungen, die ausgeben, was Sie "erwarten", um das Problem zu demonstrieren - aber das ist nur Glück.

Was Sie entdeckt haben ist eigentlich noch wichtiger ist - dass die Zahl der Ausfallarten breiter als man sich vorstellen kann (obwohl zum Glück, keine Festplatte noch neu formatiert hat) und dass die schädlichste und gefährliche Art von ' undefiniertes Verhalten ' ist das, wo das Verhalten für 99,99% der Zeit tatsächlich "wie erwartet" ist.

Es ist die 0,01%, die Sie bekommt.

Verwandte Themen