C# ';静态';值在函数调用后重置
我找到了很多关于静态的文章(、、等等),但我仍然不明白为什么这段代码返回C# ';静态';值在函数调用后重置,c#,C#,我找到了很多关于静态的文章(、、等等),但我仍然不明白为什么这段代码返回-1: class Program { static int value = 0; static int foo() { value = value - 7; return 1; } static void Main(string[] args) { value -= foo(); Console.WriteL
-1
:
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();
}
}
以下是调试器在运行foo()
之后,但在从值中减去结果之前显示的内容:
但是一步之后,值
是-1
:
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();
}
}
我希望-8
,因为静态字段存储在内存中一次
当我把它改成
var x = foo();
value -= x;
它显示-8
这到底是怎么回事?这句话
value -= foo(); // short for value = value - foo();
相当于
var temp = value; // 0
var fooResult = foo(); // 1
value = temp - fooResult; // -1
这就是为什么你会得到-1
这个问题与静态无关;这是关于减法是如何工作的
value-=foo()
可以扩展为value=value-foo()
编译器将解释为四个步骤:
将value
的值加载到堆栈上
调用方法foo
,并将结果放入堆栈
使用堆栈上的这两个值进行减法运算
将结果设置回值
字段
因此,value
字段的原始值已经加载。无论您在方法foo
中更改value
,减法的结果都不会受到影响
如果将顺序更改为value=-foo()+value
,则调用foo
后将加载value
字段的值。结果是-8
;这就是你期望得到的
感谢埃利亚胡的评论
value -= foo(); // value = value - foo();
foo()
将返回1
value
最初是0
,因此:0=0-1
现在,值具有-1
因此,问题在于返回1
只需查看生成的CIL:
.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:
-在堆栈上推送静态字段的值s:[值(0)]
IL\u 0006:
-将7
推到堆栈上s:[7,值(0)]
IL\u 0007:
-从值1(0
)中减去值2(7
),返回一个新值(-7)
IL_0008:
-将静态字段的值替换为val(值=-7)
IL\u 000d:
-将1
推到堆栈上s:[1,7,值(-7)]
IL\u 000e:
-将堆栈中的值弹出到局部变量0中。(lv=1)
IL_0011:
-将局部变量0加载到堆栈中s:[lv(1),7,值(-7)]
IL_0012:
-返回(lv(1))
以及Main
方法:
.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\u 0001:
-将值
推送到堆栈上(即0
)
IL\u 0006:
-调用foo
(将返回1
)
IL\u 000b:
-从value1(0)
中减去值:value2(1)
(value(0)-value(1)=-1
)
因此结果是-1
您可以使用菜单调试→ 窗户→ 拆解并检查后台发生的情况:
我评论了最有趣的部分
//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
因此,当编写value-=foo()
时,它确实会生成如下代码:
value = 0; // In the beginning STEP_A
//... main
var temp = value; //STEP_B
temp -= foo(); // STEP_C
value = temp; // STEP_D
我认为这与它如何在汇编级别减去值有关,这会导致程序中出现一些不一致性。我不知道这是否与静电有关。但根据我的直觉,这就是发生的情况:
让我们关注value-=foo()
保存旧的值
(推送到堆栈)
foo()
函数返回1
现在,由于foo()
操作,值为-7
问题是:用1
减去旧的值
(即先前保存的值,即0
),然后将结果分配给当前的值
@pappbence96,这正是他所期望的,但没有得到。仅供参考,这与静态无关。已对此进行调试,当方法结束时,值=-7,但下一步它神奇地在“-1”上更改。这是一个无法理解的问题,value-=foo()
与value=value(0)的当前值-foo()相同,后者返回1
,与value=0-1
相同foo()
只在调用foo()
的计算中引用了值之后,才会递减值。糟糕的解释让人难以理解,但简而言之就是这样代码>在意义上具有内在的模糊性。有些语言有适当的法律术语来定义这些语句,其他语言(臭名昭著的C族)只是说:“操作数求值的顺序是未定义的。”不管您的语言属于哪一类,这种模棱两可的代码不应该写出来,因为它很难理解。@Bahrom:看起来像你说的那样工作,但为什么呢?@Eliahuaron:因为C#规范这么说。为什么C#spec会这么说?因为(a)具有确定的评估顺序和(b)a-=b表现为a=a-b(而只评估a
一次)都被广泛认为是好主意。你会以不同的方式指定它吗?如果是,如何以及为什么?如果您将value=foo()-value
更改为value=-foo()+value
在你的最后一句话中,你将得到“预期”结果-8
@EliahuAaron…很可能。操作数的求值顺序,因此编译器可以按任意顺序或并行方式求值这两个参数。但是你的评论很有帮助地强调了-=
是一件比最初看起来更复杂的事情