Neudeklaration in Schleife



  • Hi!

    Wieso wird in dem folgenden Programm "a" bei jedem Durchlauf der While-Schleife wieder deklariert (mit Java-Debugger herausgefunden). Man bleibt doch in dem selben Namespace (aus C++, weiß nicht wie das in Java heißt).

    class Test
    {
      public static void main (String[] argv)
      {
          int i = 0 ;
    
          while (i++ < 2)
          {
            int a = 0 ;
    
            System.out.println ("a = " + a) ;
    
            a = 1 ;
          }
      }
    }
    

    Grüße,
    WilMen



  • Auch in C++ wird a jedesmal auf 0 gesetzt. Um die Bedingung der while-Schleife zu testen, verlaesst du den Schleifenkoerper. Danach gehst du wieder in den Anweisungsblock und a wird neu angelegt.



  • Hi knivil,

    dass es in C++ auch so ist, wusste ich. Mir hat nur noch eine Begründung dafür gefehlt, wieso es so ist! Die habe ich jetzt. Danke!

    Gruß,
    WilMen



  • Hi nochmal,

    ich habe das ganze nochmal in C++ folgendermaßen getestet:

    #include <iostream>
    
    using std::cout ;
    using std::endl ;
    
    int main()
    {
    	int i = 0 ;
    
    	while (i++ < 2)
    	{
    		int a ; // Deklaration 
    
    		cout << "vor (erster) Initialisierung: " << endl ;
    		cout <<	"a = " << a << " address = " << &a << endl << endl ;
    
    		a = 6 ; // (Re-)Initialisierung
    
    		cout << "nach Initialiserung: " << endl ;
    		cout <<	"a = " << a << " address = " << &a << endl << endl ;
    
    	}
    	return 0 ;
    }
    

    So sieht die Ausgabe aus:

    vor Initialisierung:
    a = 1841149680 address = 0016F8DC
    
    nach Initialiserung:
    a = 6 address = 0016F8DC
    
    vor Initialisierung:
    a = 6 address = 0016F8DC
    
    nach Initialiserung:
    a = 6 address = 0016F8DC
    

    "a" wird nicht erneut deklariert!

    Wäre "a" neu deklariert worden, würde "a" bei der dritten Ausgabe nicht den Wert 6 haben und wahrscheinlich hätte "a" dann auch eine andere Adresse.

    Entschuldigt, dass ich im Java-Forum C++-Code poste, aber wenn ich in Java "a" nicht vor der Ausgabe initialisiere dann meckert der Compiler ("variable a might not have been initialized"). Wäre nett wenn ihr mir sagt, mit welchem Parameter ich diese Warnungen abstelle. Außerdem denke ich, dass dieser Sachverhalt in Java und C++ gleich implementiert ist.

    Der Java-Debugger zeigt, zumindest standardmäßig, denke ich, immer nur die lokalen (in dem Namespace) vorhandenen Variablen an. Beim Durchlaufen der While-Schleife sah ich ja, dass "a" bei der Zeile in der das Ende der While-Schleife ("}") steht kurz verschwindet, also scheinbar der Namespace verlassen wird.

    Dieses Code-Beispiel beweist aber genau das Gegenteil. Oder der Namespace wird verlassen und "a" bleibt aus irgendwelchen Sonderregelungen trotzdem deklariert, was aber irgendwie unlogisch wäre!? Ich finde diese Frage auf jeden Fall sehr spannend. Vielleicht wird aus diesem Thread ja eine spannendes Gespräch. 🙂

    Gruß,
    WilMen



  • Deklaration ist etwas statisches, was nur im Quelltext steht. Zur Laufzeit (was du im Debugger siehst) sind nur noch Zuweisungen. Man kann also nicht sagen, die Variable wird jedesmal deklariert.

    wenn ich in Java "a" nicht vor der Ausgabe initialisiere dann meckert der Compiler ("variable a might not have been initialized"). Wäre nett wenn ihr mir sagt, mit welchem Parameter ich diese Warnungen abstelle.

    Das ist keine Warnung sondern ein Fehler, den man glücklicherweise nicht abstellen kann: Definite Assignment

    In C oder C++ ist diese erste Zuweisung nicht nötig, d.h. in "frischen" Variablen steht irgend ein Wert. Eben das, was zufällig in dem entsprechenden Speicherbereich steht.



  • was laberst du eigentlich dauernd von "namespaces"? "namespaces" gibts nur in c++ und nicht in java und haben außerdem nichts mit dem problem zu tun?



  • Ich meinte natürlich den Scope. Schau bitte im Duden nach was "labern" bedeutet, du scheinst dir über die Bedeutung nicht ganz im Klaren zu sein.



  • Las dich nicht von der Ausgabe in C++ täuschen!!

    vor Initialisierung:
    a = 1841149680 address = 0016F8DC

    nach Initialiserung:
    a = 6 address = 0016F8DC

    vor Initialisierung:
    a = 6 address = 0016F8DC

    nach Initialiserung:
    a = 6 address = 0016F8DC

    Beim ersten Durchlauf ist a nicht initialisiert (1841149680) da steht halt auf Adresse 0016F8DC irgendwas.

    Dann wird a 6 zugewiesen und gelöscht. Dann wird wieder a deklariert und er zeigt auf die alte Speicherzelle 0016F8DC. Da steht halt noch der Wert von vorhin drin da der Wert beim löschen nicht überschrieben wird.



  • Durchlesen und verstehen, zumindestens den ersten Teil: Continuations for Curmudgeons.

    Und: Namesraeume gibt es in jeder Sprache, also auch namespaces wenn man denglish redet.



  • Nach "The C++ Programming Language" gibt es drei Sorten von Speichermanagement: Static Memory, Automatic Memory und Free Store.

    Bei "a" aus dem Beispiel müsste es sich um Automatic Memory handeln, da es sich um eine lokale Variable handelt. Ihr Speicher müsste eigentlich automatisch angelegt und gelöscht werden, was hier aber scheinbar nicht der Fall ist, da der Wert 6 erhalten bleibt.

    Desweiteren wird unter Static Memory explizit gesagt, dass sich die Adresse von statischen Variablen nicht ändert! Wieso sollte dies extra genannt werden, wenn es sich bei Automatic Memory diesbezüglich (,also, dass die Adresse gleich bleibt,) ebenso verhält?

    Gruß,
    WilMen



  • WilMen schrieb:

    Ihr Speicher müsste eigentlich automatisch angelegt und gelöscht werden, was hier aber scheinbar nicht der Fall ist, da der Wert 6 erhalten bleibt.

    Du hast Recht, aber dass die 6 erhalten bleibt, ist nicht vom Standard garantiert und sehr vom Compiler (und dessen Einstellungen, der Umgebung, dem konkreten Fall usw.) abhängig.

    Um's einfach zu sagen: In diesem konkreten Fall hat sich dein Compiler dazu entschieden, bei jedem Schleifendurchlauf für die Variable den selben Speicherbereich zu nehmen und den Wert zwischen Deklaration und erster Zuweisung nicht zu ändern (wieso auch). Auf dem Speicherplatz ändert sich der Wert nicht, da ihn niemand ändert.



  • Hi Badestrand,

    ok es liegt also am Compiler, aber wieso bleibt der Wert 6 sogar erhalten, wenn ich die While-Schleife verlasse und wieder in sie eintrete. Ich habe das mal mit goto getestet. Das muss doch im Standard geregelt sein?

    //...
    
    start:
    
    while (i++ < 2)
    {
    //...
    }
    
    goto start ;
    
    // ...
    

    Gruß,
    Willi



  • WilMen schrieb:

    ok es liegt also am Compiler, aber wieso bleibt der Wert 6 sogar erhalten, wenn ich die While-Schleife verlasse und wieder in sie eintrete. Ich habe das mal mit goto getestet. Das muss doch im Standard geregelt sein?

    Ja, das Verhalten ist vom Standard abgedeckt. Und zwar hast du in der while-Schleife bei der Deklaration von a keinen Initialwert angegeben, womit du dem Compiler sagst, dass es dir egal ist, welchen Wert a nach der Deklaration hat. Ob da noch alte Werte drinstehen, oder der Compiler die Variable nach der Deklaration auf 0 oder einen anderen Wert setzt, bleibt ihm überlassen. Den Speicherbereich unangetastet zu lassen, ist halt effizienter, kostet idR eine Prozessor-Instruktion weniger.

    Oder mal andersherum: Du erwartest wahrscheinlich, dass der Speicherbereich nach der Deklaration "gelöscht" oder "leer" ist. Das Ding ist, ein Speicherbereich kann nicht gelöscht oder leer sein, irgendwas steht immer drin. 0 ist zwar der "neutrale" Wert eines 'int's, aber für den Compiler eben auch nur irgendein Wert.

    Nochmal anders: Bei der Deklaration von Variablen wird normalerweise der Standardkonstruktor aufgerufen. In diesem Szenario

    struct MyInt
    {
        // Standardkonstruktor (Null Argumente), in der Initialisierungsliste wird i auf 0 gesetzt
        MyInt() : i(0)
        {
        }
    
        int i;
    };
    
    while (... )
    {
        MyInt a;
        ...
    }
    

    würde "i" aus "a" bei jedem Schleifendurchlauf erneut auf 0 gesetzt werden (eben weil jedesmal der Standardkonstruktor von MyInt aufgerufen wird). Bei den nativen Datentypen wie int, float, Zeigern, usw wird aber nur der Speicher reserviert und nicht der Konstruktor aufgerufen.



  • OK, danke!

    Ich finde es nur seltsam, dass sich "a" wie eine statische Variable verhält, ob ich nun

    auto int a ;
    
    static int a ;
    

    schreibe, scheint (zumindest in diesem Fall) keinen Unterschied zu machen. Es liegt also am Compiler.

    Eine Sache finde ich trotzdem noch nicht logisch. Wieso wird der Schleifenkörper (also dieser Scope) eigentlich verlassen, wenn die Bedingung geprüft wird? Ein Block der auf

    while(/* ... */)
    

    (ohne Semikolon hinter der While-Bedingung) folgt, gehört doch immer zu der Schleife. Das wird sicherlich auch so im Standard geregelt sein, erklär mir bitte nur, was man sich dabei gedacht haben könnte bzw. hat.

    Gruß,
    WilMen



  • WilMen schrieb:

    Das wird sicherlich auch so im Standard geregelt sein, erklär mir bitte nur, was man sich dabei gedacht haben könnte bzw. hat.

    Ich denke mal, du erwartest zuviel was im Standard geregelt sein könnte.

    Das was nicht geregelt werden braucht, wurde auch nicht geregelt
    um den Compiler-Entwicklern größtmöglichen Freiraum zu bieten.


Anmelden zum Antworten