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语句中吧。。。
  • 相关阅读:
    赫尔维茨公式
    从解析几何的角度分析二次型
    Struts 1 Struts 2
    记一次服务器被入侵的调查取证
    契约式设计 契约式编程 Design by contract
    lsblk df
    Linux Find Out Last System Reboot Time and Date Command 登录安全 开关机 记录 帐号审计 历史记录命令条数
    Infrastructure for container projects.
    更新文档 版本控制 多版本并发控制
    Building Microservices: Using an API Gateway
  • 原文地址:https://www.cnblogs.com/zhang740/p/3811651.html
Copyright © 2011-2022 走看看