zoukankan      html  css  js  c++  java
  • c += c-- | ++b;

    一切都是从这开始的

    一个大一学弟通过QQ给我发来一个C++的题:

    int c = 8, b = 3;
    c += c-- | ++b;
    

    问c的值是多少。通过笔算得到c为19,然后随手建了个C#控制台项目跑了一下,悲剧了。。。C#输出的为20。重新笔算一遍还是19啊,赶紧重新建了一个C++控制台项目跑出的结果为19。到底为什么C++和C#会不一样呢?

    求证1

    通过网上查资料得知,是C#求值顺序的问题,具体是怎么样的情况呢?我们来反汇编一下:

    	.method private hidebysig static void  Main(string[] args) cil managed
    	{
    	  .entrypoint
    	  // Code size       33 (0x21)
    	  .maxstack  4
    	  .locals init ([0] int32 c,
    	           [1] int32 b)
    	  IL_0000:  nop
    	  IL_0001:  ldc.i4.8
    	  IL_0002:  stloc.0
    	  IL_0003:  ldc.i4.3
    	  IL_0004:  stloc.1
    	  IL_0005:  ldloc.0
    	  IL_0006:  ldloc.0
    	  IL_0007:  dup
    	  IL_0008:  ldc.i4.1
    	  IL_0009:  sub
    	  IL_000a:  stloc.0
    	  IL_000b:  ldloc.1
    	  IL_000c:  ldc.i4.1
    	  IL_000d:  add
    	  IL_000e:  dup
    	  IL_000f:  stloc.1
    	  IL_0010:  or
    	  IL_0011:  add
    	  IL_0012:  stloc.0
    	  IL_0013:  ldloc.0
    	  IL_0014:  call       void [mscorlib]System.Console::WriteLine(int32)
    	  IL_0019:  nop
    	  IL_001a:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
    	  IL_001f:  pop
    	  IL_0020:  ret
    	} // end of method Program::Main
    

    通过IL代码我们可以清晰的看到他的计算过程:

    • 0~4行 为变量赋值Call Stack里0为变量c值为8,1为变量b值为3
    • 5行 缓存了c的值,放到了Evaluation Stack里,缓存的值为8
    • 6行 缓存了一个c值,缓存的值为8
    • 7~a行 执行了c=c-1,此时c值为7
    • b~f行 执行了b=b+1,并留了一个副本,值为4
    • 10行 6行缓存的数8和b~f行中留的4做or,值为12
    • 11行10行中结果12再加5行中缓存的8结果为20
    • 12行存储到变量c中

    c += c-- | ++b; 等价于 c = c + (c-- | ++b);,通过反汇编我们可以看出:

    1. C#的求值顺序为从左到右不会因为运算顺序改变,等号右侧第一个c的值在一开始就缓存了。
    2. c--在求值之后立刻就进行了结算,c变量此时值变为了7,但是c变量的值并不影响算式最终的结果,原因见1。
    3. 把算式变为c = c + (--c | ++b);得到的值为15(8+(7|4)),反汇编后观察,结论同第一条,c--和--c只影响了括号中的运算结果。

    根据上面结论我们把算式改成c = (c-- | ++b) + c;,得到的结果为19。

    求证2

    那么C++到底是怎么执行的呢?继续,反编译之:

    (上图反汇编的程序基于VS2013的C++ Debug编译结果,GCC 4.6.1的反汇编代码略有区别,执行过程一致,结论仅限定在这两个编译环境下)
    C++的执行过程:

    1. 使用eax寄存器做b=b+1
    2. 使用ecx寄存器做c=8+(8|4)(此时b=4)
    3. 使用edx寄存器做c=c-1

    从反汇编可以看出:

    • C++不会缓存数值,C++也没有规定求值顺序。
    • C++在计算的时候才取值,所以c = (c-- | ++b) + c;的结果还是19。
    • C++是在整个算式结束的时候才进行的c--,也就是说之所以结果是19不是20,不是因为先算括号中的or造成最后的加法中的c为7,而是因为c--是在算式赋值结束后才进行结算。(老师们,你们教对了么?)

    最后验证一下,改变算式为c = c * 2 + (c-- | ++b);C++的输出结果为27,8*2+12=28-1=27,反汇编也可以看到最后才执行的sub,图就不上了。

    最后

    • 如果你在学C++,千万不要用C#来验证你的作业题答案。。。
    • 就让‘++’这种东西只出现在for语句中吧。。。
  • 相关阅读:
    【2017 Multi-University Training Contest
    【2017 Multi-University Training Contest
    【Uva 1543】Telescope
    Information centric network (icn) node based on switch and network process using the node
    JQuery实现放大镜
    JQuery实现放大镜
    JS关闭浏览器 (不弹出提示框)
    JS关闭浏览器 (不弹出提示框)
    JS关闭浏览器 (不弹出提示框)
    MVC5 Controller构造方法获取User为空解决方法
  • 原文地址:https://www.cnblogs.com/zhang740/p/3811651.html
Copyright © 2011-2022 走看看