2016-03-08 7 views
13

Warum wird im folgenden Code die Variable i nicht mit dem Wert 1 belegt?Variablendefinition innerhalb der Schalteranweisung

#include <stdio.h>  

int main(void) 
{ 
    int val = 0; 
    switch (val) {   
     int i = 1; //i is defined here 

     case 0: 
      printf("value: %d\n", i); 
      break; 
     default: 
      printf("value: %d\n", i); 
      break; 
    } 
    return 0; 
} 

Wenn ich kompilieren, erhalte ich eine Warnung über i trotz int i = 1; initialisiert wird, dass es deutlich

$ gcc -Wall test.c 
warning: ‘i’ is used uninitialized in this function [-Wuninitialized] 
    printf("value %d\n", i); 
    ^

Wenn val = 0 initialisiert, dann wird der Ausgang 0 ist.

Wenn val = 1 oder irgendetwas anderes, dann ist auch der Ausgang 0.

mir bitte erklären, warum die Variable i deklariert, aber nicht innerhalb des Schalters definiert. Das Objekt mit der ID i existiert mit der automatischen Speicherdauer (innerhalb des Blocks), wird jedoch niemals initialisiert. Warum?

+0

Ich frage nach vor dem Fall Etikett Variablendefinition Freund. Weil ich versuche, zu benutzen, ist ich lokale Variable innerhalb des Schalters. – sakthi

+3

Schließen Sie bitte nicht als C++ - Duplikat. Finden Sie eine C-Version. – 2501

+2

Dies ist kein Duplikat des Posts von @ user3121023, da in der verknüpften Frage die Deklaration von "i" in ** eine "case" -Anweisung ist, also wickle man sie einfach um Klammern. In diesem Fall steht die Deklaration von "i" außerhalb jeder "case" -Anweisung, ich bin mir nicht sicher, ob dies gültig ist. – Holt

Antwort

4

In dem Fall, in dem val nicht Null ist, springt die Ausführung direkt zum Label-Standardwert. Dies bedeutet, dass die Variable i, während sie im Block definiert ist, nicht initialisiert ist und ihr Wert nicht bestimmt ist.

6.8.2.4 Die switch-Anweisung

  1. ein switch-Anweisung bewirkt, um zu springen, zu steuern, in denen oder hinter der Anweisung, die die Schalterkörper, an dem je Wert eines steuernden Ausdrucks und das Vorhandensein eines Standardlabels und die Werte von beliebigen Falllabels auf oder in dem Schalterkörper. Ein Case oder Standard-Label ist nur innerhalb der nächsten umschließenden Switch-Anweisung zugänglich.
3

Tat Ihre i innerhalb des switch Block deklariert wird, so dass es im Inneren des switch existiert nur. Allerdings ist seine Initialisierung nie erreicht, so dass es nicht initialisierte bleibt, wenn val nicht 0 ist

Es ist ein bisschen wie der folgenden Code:

{ 
    int i; 
    if (val==0) goto zerovalued; 
    else goto nonzerovalued; 
    i=1; // statement never reached 
    zerovalued: 
    i = 10; 
    printf("value:%d\n",i); 
    goto next; 
    nonzerovalued: 
    printf("value:%d\n",i); 
    goto next; 
    next: 
    return 0; 
} 

Intuitiv roher Erklärung denken wie der Compiler für einige fragen Ort (im Call-Frame in Ihrem Call-Stack oder in einem Register oder was auch immer), und stellen Sie sich die Initialisierung als Zuweisungsanweisung vor. Beide sind separate Schritte, und Sie könnten eine Initialisierungsdeklaration in C wie int i=1; als syntaktischen Zucker für die rohe Deklaration int i; gefolgt von der Initialisierungszuordnung i=1; betrachten.

(tatsächlich sind die Dinge ein wenig komplizierter und beispielsweise mit int i= i!=i; noch komplexer in C++)

1

Linie zur Initialisierung von i variable int i = 1; wird nie aufgerufen, da es zu keinem der verfügbaren Fälle gehört.

+2

Eine Initialisierung muss nicht "aufgerufen" werden, um geschehen zu können. Denken Sie an globale Variablen. –

12

Gemäß der C - Norm (6.8-Anweisungen und Blöcke), emphasis Mine:

3 ein Block einen Satz von Deklarationen und Anweisungen ermöglicht in eine syntaktische Einheit zusammengefasst werden. Die Initialisierungen von Objekten, die automatische Speicherdauer und die variable Länge Array Deklaratoren gewöhnlicher Identifikatoren mit Block Umfang, werden ausgewertet und die Werte sind in den Objekten (einschließlich Speichern eines unbestimmten Wert in Objekten ohne Initialisierer gespeicherten) Jedes Mal, wenn die Deklaration in der Reihenfolge der Ausführung erreicht wird, als ob es eine Anweisung wäre, und innerhalb jeder Deklaration in der Reihenfolge, die Deklaratoren angezeigt werden.

Und (6.8.4.2 Die switch-Anweisung)

4 Ein Schalter Anweisung bewirkt steuern zu springen, in oder hinter der Anweisung, die den Schalterkörper ist, auf dem Wert, je eines Steuerausdrucks, und auf das Vorhandensein eines Standardlabels und die Werte von allen Fall Etiketten auf oder in der Schalterkörper. Ein Fall oder Standard Etikett ist nur innerhalb der nächsten umschließenden Schalter Anweisung zugänglich.

So ist die Initialisierung der Variablen i nie, weil die Erklärung

switch (val) {   
     int i = 1; //i is defined here 
     //... 

erreicht wird wegen Sprüngen in der Reihenfolge der Ausführung ausgewertet Etiketten Fall und wie jede Variable mit der automatischen Speicherdauer unbestimmt hat Wert.

Siehe auch dieses normativen Beispiel aus 6.8.4.2/7:

BEISPIEL im künstlichen Programmfragment

switch (expr) 
{ 
    int i = 4; 
    f(i); 

case 0: 
    i = 17; /* falls through into default code */ 
default: 
    printf("%d\n", i); 
} 

das Objekt, dessen Identifikator i besteht mit automatischer Speicherdauer (innerhalb des Blocks), aber wird niemals initialisiert, und wenn der steuernde Ausdruck einen Wert ungleich null hat, wird der Aufruf der printf-Funktion auf eine Indetermina zugreifen te Wert. Ebenso kann der Aufruf der Funktion f nicht erreicht werden.

+1

Schöne Antwort, ich denke der Teil von 6.8 ist notwendig um dies zu erklären. Könntest du zufällig das Beispiel nehmen, das ich in meiner Antwort zitiert habe, und es deiner Antwort hinzufügen? (Da es ein normatives Beispiel ist. Fühlen Sie sich frei, das Formatierungskram von meiner Antwort zu kopieren/einzufügen) Dann lösche ich meine Antwort zugunsten Ihrer. – Lundin

+0

Ich denke, der entscheidende Teil ist "als wäre es eine Aussage". Es sollte noch mehr hervorstechen. –

+1

Tatsächlich ist jede Antwort, die die automatische Speicherdauer nicht erwähnt, unvollständig. Denn wenn Sie "i" als "statisch" deklarieren, dann voila, verhält sich der Code plötzlich wie erwartet. Dies bedeutet, dass dies nicht so sehr mit dem Verhalten der switch-Anweisung zu tun hat, wie mit der Regel der Initialisierung von Variablen mit automatischer Speicherdauer. – Lundin

1

Die Initialisierung von Variablen mit automatischer Speicherdauer detailliert in C11 6.2.4p6:

  1. Für ein solches Objekt, das nicht eine variable Länge Array-Typ hat, seine Lebensdauer erstreckt sich vom Eintritt in den Block, dem es zugeordnet ist, bis die Ausführung dieses Blocks in irgendeiner Weise endet. (Wenn Sie einen geschlossenen Block eingeben oder eine Funktion aufrufen, wird die Ausführung des aktuellen Blocks angehalten, aber nicht beendet.) Wenn der Block rekursiv eingegeben wird, wird jedes Mal eine neue Instanz des Objekts erstellt. Der Anfangswert des Objekts ist unbestimmt.Wenn für das Objekt eine Initialisierung angegeben ist, wird es jedes Mal ausgeführt, wenn die Deklaration oder das zusammengesetzte Literal bei der Ausführung des Blocks erreicht wird; Andernfalls wird der Wert jedes Mal unbestimmt, wenn die Deklaration erreicht wird.

D.h. die Lebensdauer von i in

switch(a) { 
    int i = 2; 
    case 1: printf("%d",i); 
      break; 
    default: printf("Hello\n"); 
} 

ist {-}. Sein Wert ist unbestimmt, , es sei denn, die Deklaration int i = 2; ist in der Ausführung des Blocks erreicht. Da die Deklaration vor jedem Case-Label steht, kann die Deklaration niemals erreicht werden, da die switch auf das entsprechende Case-Label springt - und über die Initialisierung hinausgeht.

Daher bleibt i nicht initialisiert. Und da es der Fall ist, und da es seine Adresse nie genommen hat, die Verwendung des nicht initialisierten Wert undefinierten VerhaltenC11 6.3.2.1p2:

  1. [...] Wenn der L-Wert bezeichnet ein Objekt der automatischen Speicherdauer, das mit der Speicherklasse des Registers deklariert werden könnte (seine Adresse wurde nie vergeben), und dieses Objekt ist nicht initialisiert (wird nicht mit einem Initialisierer deklariert und vor der Verwendung wurde ihm keine Zuweisung zugewiesen), Verhalten ist nicht definiert.

(Beachten Sie, dass der Standard selbst hier Worte der Inhalt in der Klärung Klammer falsch - es mit einem Initialisierer deklariert, aber die Initialisierung nicht ausgeführt wird).

Verwandte Themen