• i++与++i


    i++与++i  

    请问下列的结果是甚么呢? 

    i=1 
    (一) 
    k=(i++)+(i++)+(i++) 

    (二) 
    k=(++i)+(++i)+(++i)

     

    回答:

    这样的表达式的求值顺序是没有定义的。因为分号是一个顺序点,而二元加号不是一个顺序点,相邻顺序点之间的表达式的求值顺序是任意的,并且它们的副作用只在下一个顺序点之后才生成。不管是 
    k = (i++) + (i++) + (i++); 
    还是 
    k = (++i) + (++i) + (++i); 
    这里分号是一个顺序点,理论上所有++操作和赋值操作的副作用都要在分号之后才生成。但是这一点并不是强制编译器必须要等到分号结束之后才执行赋值操作,只是规定相邻两个顺序点之间的表达式求值所产生的副作用只有等到下一个顺序点结束才是确定的。而在这个顺序点结束之前,编译器对位于该个顺序点和前一个顺序点之间的表达式以任意顺序求值。就是说,你上面的这每个表达式中的三个++i(或者i++)的求值顺序是任意的,编译器想先做哪个就先做哪个。而每做一个,你都想依赖另一个的副作用。但副作用要到分毫结束才是确定的,所以这个时候编译器想怎样取舍都是可以的,不管++i还是i++它都既可以取没加1之前的值也可以取加1后的值来作为++i或者i++的值,这样每次都有不同的取舍,等到分号结束,你得到的k的值当然就可能有不同的情况了。你这两个表达式都违反了C语言有关顺序点的规定,编译器给出的值也具有一定的任意性,没有人能确切地告诉你它们的值,你只能去问编译器。不同的编译器可能会给出不同的值,就像楼上的两位得到的结果一样。如果,对上面的解释不太明白,你就应该好好参考一下顺序点的定义和作用了。总之,像i++(或者++i)这类含副作用的表达式,不要揉在一块儿,宁可多声明几个变量都好: 
    int r, s, t; 
    r = ++i; 
    s = ++i; 
    t = ++i; 
    k = r + s + t;

     

    ++与++i的“++”操作符,C或C++是为了方便实现类似循环里简单自增变量而设的,本意是想简化编程,提高运行速度,但现实编程中与降低程序可度性的代价相比,这点“方便”可以说是用处不大,甚至可以说是危险的。

     

    int i=2;
       int s=0;
       s=i++ + ++i;

       其中,两个子式(i++)和(++i)进行和计算,按理说就直接分别计算两个子式的值,加在一起不就完了嘛,那我们来看看这样计算的结果。左边i++先赋值再自增,那么子式值就是2,右边++i先自增再赋值,那么就是3,2+3应该等于5。但是用VC运行后发现,结果等于6!这到底是怎么回事?别急,先来看看VC编译器的汇编代码:

      120:      int i=2;
    0040165A   mov         dword ptr [ebp-44h],2    
      121:      int s=0;
    00401661   mov         dword ptr [ebp-48h],0
      122:      s=i++ + ++i;
    00401668   mov         eax,dword ptr [ebp-44h]
    0040166B   add         eax,1
    0040166E   mov         dword ptr [ebp-44h],eax
    00401671   mov         ecx,dword ptr [ebp-44h]
    00401674   add         ecx,dword ptr [ebp-44h]
    00401677   mov         dword ptr [ebp-48h],ecx
    0040167A   mov         edx,dword ptr [ebp-44h]
    0040167D   add         edx,1
    00401680   mov         dword ptr [ebp-44h],edx

       看不懂?呵呵,希望我解释了以后能让你对汇编提高点兴趣~~  
    00401668   mov         eax,dword ptr [ebp-44h]
    0040166B   add         eax,1
    0040166E   mov         dword ptr [ebp-44h],eax
       这三句的意思是从内存地址(i)中拿出值来放入寄存器中,然后自加1后再存入原来的内存地址中,整个过程就是++i的实现,可以确定的是,i的值现在是3。接着往下看:
    0401671   mov          ecx,dword ptr [ebp-44h]
    00401674   add         ecx,dword ptr [ebp-44h]
    00401677   mov         dword ptr [ebp-48h],ecx
       上面是说i的值取出来后放入ECX,再加上i的值后传给S,实际上就是s=i+i的意思。
    0040167A   mov         edx,dword ptr [ebp-44h]
    0040167D   add         edx,1
    00401680   mov         dword ptr [ebp-44h],edx
       最后的三句和第一段是一样的,就是i的值加1。整理一下顺序,就是:先i自增,然后用加1后的i值再与自己相加(i乘以2),加的值赋给S。最后结束了以后i再自加1。这样看来,编译器并没有遵守所谓的“君子协定”,没有按照从左到右的顺序结合子式。既然官方的说不通,就用一下我的结论吧,“有增先增,无增结合”,由于表达式中“有增”,就是++i,那么不管它的位置在哪,先加了再说,加完后,剩下的哪个不属于先增的,那就结合吧,于是就有了i+i的结合,结合后的值就是最终S的结果。至于i++,抢不过人家先增的++i,那就只好最后增喽~~
       这个例子很简单,只有两个子式,也只能简单说一下编译器是如何看待++i和i++的,至于比较复杂的++运算就放在下一章讲,顺便说一下我的“树”型计算方法,对于此类运算比较实用,希望大家能够喜欢~~

  • 相关阅读:
    WYT的刷子
    小烈送菜
    猴腮雷
    基于Docker的Mysql主从复制搭建
    C#集合类型大揭秘
    ASP.NET三剑客 HttpApplication HttpModule HttpHandler 解析
    使用缓存的正确姿势
    【模块化那些事】 拆散的模块化
    分享一个开源的网盘下载工具BaiduPCS-Go
    【抽象那些事】不必要的抽象
  • 原文地址:https://www.cnblogs.com/jackrex/p/3001315.html
Copyright © 2020-2023  润新知