c# - 'statischer' Wert scheint nach Funktionsaufruf zurückgesetzt zu werden




(4)

Diese Frage hat hier bereits eine Antwort:

Ich habe viele Artikel über Statik gefunden ( MSDN , MSDN 2 , Stack Overflow und vieles mehr), kann aber immer noch nicht verstehen, warum dieser Code -1 zurückgibt:

class Program
{
    static int value = 0;

    static int foo()
    {
        value = value - 7;
        return 1;
    }

    static void Main(string[] args)
    {
        value -= foo();
        Console.WriteLine(value);
        Console.ReadKey();
    }
}

Folgendes zeigt der Debugger, nachdem foo() ausgeführt wurde, aber bevor das Ergebnis vom value abgezogen value :

Aber einen Schritt später ist der value -1 :

Ich würde -8 wegen des statischen Feldes erwarten, das im Speicher einmal gespeichert wird.

Als ich es geändert habe

var x = foo();
value -= x;

es zeigt -8

Wie funktioniert das genau?


Bei diesem Problem geht es nicht um statische Aufladung. Es geht darum, wie die Subtraktion funktioniert.

value -= foo(); kann erweitert werden zu value = value - foo()

Der Compiler erklärt es in vier Schritten:

  1. Laden Sie den Wert von value auf den Stapel.
  2. Rufe die Methode foo und lege das Ergebnis auf den Stapel.
  3. Subtrahieren Sie mit diesen beiden Werten auf dem Stapel.
  4. Setzen Sie das Ergebnis zurück auf das value .

Der ursprüngliche Wert des value ist also bereits geladen. Unabhängig davon, welchen value in der Methode foo ändern, wird das Ergebnis der Subtraktion nicht beeinflusst.

Wenn Sie die Reihenfolge in value = - foo() + value ändern, wird das Feld value of value geladen, nachdem foo aufgerufen wurde. Das Ergebnis ist -8 ; das wird von dir erwartet.

Danke für Eliahus Kommentar.


Die Aussage

value -= foo(); // short for value = value - foo();

ist äquivalent zu

var temp = value; // 0
var fooResult = foo(); // 1
value = temp - fooResult; // -1

Deshalb bekommst du -1


Schauen Sie sich die generierte CIL an:

.method private hidebysig static int32  foo() cil managed
{
  // Code size       19 (0x13)
  .maxstack  2
  .locals init ([0] int32 V_0)
  IL_0000:  nop
  IL_0001:  ldsfld     int32 Program::'value'
  IL_0006:  ldc.i4.7
  IL_0007:  sub
  IL_0008:  stsfld     int32 Program::'value'
  IL_000d:  ldc.i4.1
  IL_000e:  stloc.0
  IL_000f:  br.s       IL_0011
  IL_0011:  ldloc.0
  IL_0012:  ret
} // end of method Program::foo
  • IL_0001: - Den Wert des statischen Feldes auf den Stapel IL_0001: . s: [Wert (0)]
  • IL_0006: - 7 auf den Stapel IL_0006: . s: [7, Wert (0)]
  • IL_0007: - Subtrahiert Wert2 ( 7 ) von Wert1 ( 0 ) und gibt einen neuen Wert (-7) zurück.
  • IL_0008: - Ersetzt den Wert des statischen Feldes durch val (Wert = -7) .
  • IL_000d: - 1 auf den Stapel IL_000d: . s: [1, 7, Wert (-7)]
  • IL_000e: - IL_000e: einen Wert aus dem Stack in die lokale Variable 0 ein. (Lv = 1)
  • IL_0011: - Laden Sie die lokale Variable 0 in den Stack. s: [lv (1), 7, Wert (-7)]
  • IL_0012: - Return (lv (1))

Und die Hauptmethode:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       29 (0x1d)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldsfld     int32 Program::'value'
  IL_0006:  call       int32 Program::foo()
  IL_000b:  sub
  IL_000c:  stsfld     int32 Program::'value'
  IL_0011:  ldsfld     int32 Program::'value'
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_001b:  nop
  IL_001c:  ret
} // end of method Program::Main
  • IL_0001: - schiebt den value auf den Stack (der 0 )
  • IL_0006: - ruft foo (was 1 IL_0006: )
  • IL_000b: - subtrahiere Werte: value2(1) von value1(0) ( value(0) - value(1) = -1 ).

Das Ergebnis ist also -1 .


Sie können das Menü Debuggen Windows Zerlegen verwenden und überprüfen, was im Hintergrund vor sich geht:

Ich habe die interessantesten Teile kommentiert.

    //static int value = 0;
    05750449  mov         ebp,esp
    0575044B  push        edi
    0575044C  push        esi
    0575044D  push        ebx
    0575044E  sub         esp,2Ch
    05750451  xor         edx,edx
    05750453  mov         dword ptr [ebp-10h],edx
    05750456  mov         dword ptr [ebp-1Ch],edx
    05750459  cmp         dword ptr ds:[15E42D8h],0
    05750460  je          05750467
    05750462  call        55884370
    05750467  xor         edx,edx
    05750469  mov         dword ptr ds:[15E440Ch],edx  // STEP_A place 0 in ds register
 somewhere
    0575046F  nop
    05750470  lea         esp,[ebp-0Ch]
    05750473  pop         ebx
    05750474  pop         esi
    05750475  pop         edi
    05750476  pop         ebp
    05750477  ret

    //value -= foo();
    057504AB  mov         eax,dword ptr ds:[015E440Ch]   // STEP_B places (temp) to eax. eax now contains 0
    057504B0  mov         dword ptr [ebp-40h],eax
    057504B3  call        05750038



    057504B8  mov         dword ptr [ebp-44h],eax
    057504BB  mov         eax,dword ptr [ebp-40h]
    057504BE  sub         eax,dword ptr [ebp-44h]   //STEP_C substract the return(-1) of call from the temp eax
    057504C1  mov         dword ptr ds:[015E440Ch],eax  // STEP_D moves eax (-1) value to our ds register to some memory location

    //Console.WriteLine(value);
    015E04C6  mov         ecx,dword ptr ds:[015E440Ch]  // Self explanatory; move our ds(-1) to ecx, and then print it out to the screen.
    015E04CC  call        54CE8CBC

Es stimmt also, dass beim Schreiben von value -= foo() Code in etwa wie folgt generiert wird:

value = 0; // In the beginning STEP_A

//... main
var temp = value; //STEP_B
temp -= foo(); // STEP_C
value = temp; // STEP_D



c#  

c#