精华内容
下载资源
问答
  • i++和++i的真正区别

    万次阅读 多人点赞 2017-10-11 23:21:20
    记得刚开始学编程的适合是从c语言开始的,还是看的谭浩强的那本书,上面对介绍i++和++i的区别如下: i++是先赋值,然后再自增;++i是先自增,后赋值。 用代码表示就是: 若 a = i++; 则等价于 a=i;i=i+1; 而 a =...

    问题描述

    • 记得刚开始学编程的时候还是从c语言开始的,还是看的谭浩强写的那本书,上面对介绍i++和++i的区别如下:
      i++是先赋值,然后再自增;++i是先自增,后赋值。
      用代码表示就是:
      若 a = i++; 则等价于 a=i;i=i+1;
      而 a = ++i; 则等价于 i=i+1;a=i;

    • 那么事实真是这样么,只是曾经我深信不疑,但是直到我看到下面这段代码:

        @Test
        public void test(){
            int i = 0;
            i=i++;
            System.out.println(i);
        }
      

      如果按原先定义,就应该是i=i;i=i+1; 那么结果就应该是1;但是很遗憾结果是0;所以得知原先定义有误,至少是不准确的。

    模拟++的机制

    那么真实的机制是怎么样的呢?我简单用代码模拟一下它的效果:

        int i;
     
        @Test
        public void testAddI() {
            i = 0;
            i = lastAdd();
            System.out.println(i);
            i = 0;
            i = firstAdd();
            System.out.println(i);
        }
    
    
        //模拟i++的机制
        public int lastAdd() {
    	    //操作i前对其值保留副本
            int temp = i;
            i = i + 1;
            //返回副本
            return temp;
        }
    
        //模拟++i的机制
        public int firstAdd() {
            i = i + 1;
            return i;
        }
    

    输出结果为0和1,和i=i++以及i=++i的结果一致。

    通过以上代码模拟,似乎在java的执行过程中,i++和++i都直接对i进行了i=i+1的操作,但是不同的是i++得到的是i未进行加法操作的前的值的副本,而++i直接得到计算后的值。那么,事实真的是这样吗,我们再去刨析一下源码,看看在汇编指令中,它到底是怎么做的。

    源码解析

    再写一个类,源码如下:

    public class PlusI {
    
        public void iPlusPlus(){
        	int i = 0;
            i++;
        }
    
        public void plusPlusI(){
        	int i = 0;
            ++i;
        }
    
    }
    

    对其class文件进行反汇编后,代码如下:

    public class com.aliencat.javabase.bit.PlusI {
    
      public com.aliencat.javabase.bit.PlusI();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public void iPlusPlus();
        Code:
    		0 iconst_0
    		1 istore_1
    		2 iinc 1 by 1
    		5 return
    
      public void plusPlusI();
        Code:
    		0 iconst_0
    		1 istore_1
    		2 iinc 1 by 1
    		5 return
    
    }
    

    先不谈这些汇编指令的意义,乍一看,两个方法的执行指令完全一样。
    我们再把代码改下,看看为什么i=i++和i=++i会产生不一样的结果:

    public class PlusI {
    
        public void iPlusPlus(){
            int i = 0;
            i = i++;
        }
    
        public void plusPlusI(){
            int i = 0;
            i = ++i;
        }
    }
    

    对其进行反汇编后,代码如下:

    public class com.aliencat.javabase.bit.PlusI {
    
      public com.aliencat.javabase.bit.PlusI();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public void iPlusPlus();
        Code:
    		0 iconst_0
    		1 istore_1
    		2 iload_1
    		3 iinc 1 by 1
    		6 istore_1
    		7 return
    
      public void plusPlusI();
        Code:
    		0 iconst_0
    		1 istore_1
    		2 iinc 1 by 1
    		5 iload_1
    		6 istore_1
    		7 return
    
    }
    

    关于汇编指令的解析请参考:通过jvm指令手册看懂java反汇编源码

    关于方法在JVM的内存模型请参考:一文看懂Java内存模型(JMM)

    通过比较我们发现,除了iload_1iinc 1 by 1这两条指令顺序有区别外,其它都是一致的。
    下面我们来分析一下每条汇编指令的意义:

    • iconst_0:将int类型的0值压入操作数栈

    • istore_1: 弹出操作数栈顶的值赋给局部变量表下标为1的变量

    • iload_1: 将局部变量表下标为1的位置存储的值压入操作数栈

    • iinc 1 by 1:取局部变量表下标为1的位置存储的值加上1

    • istore_1:弹出操作数栈顶的值赋给局部变量表下标为1的变量

    指令图解

    下面是关于i=i++;执行过程的图解:

    1.方法执行前在内存中的的数据结构

    因为实例方法的局部变量表中默认第一个是保存的this,所以i的下标位置为1
    在这里插入图片描述

    2.执行iconst_0

    将int类型的0值压入操作数栈

    在这里插入图片描述

    3.执行istore_1

    弹出操作数栈顶的值赋给局部变量表下标为1的变量

    在这里插入图片描述

    4.执行iload_1

    将局部变量表下标为1的位置存储的值压入操作数栈

    在这里插入图片描述

    5.执行iinc 1 by 1

    取局部变量表下标为1的位置存储的值加上1(指令中第一个1代表局部变量表的下标)

    在这里插入图片描述

    6.执行istore_1

    弹出操作数栈顶的值赋给局部变量表下标为1的变量

    在这里插入图片描述

    下面是关于i=++i;的图解,我就不一一解释了
    在这里插入图片描述

    结论

    • 在使用i=i++的过程中,它会先把i的原始值0复制到操作数栈中,然后再对局部变量表中的0进行+1操作使得i变为了1,此时操作数栈顶的值为0,然后执行赋值操作时候使用的是弹出的操作数栈顶的值,所以最后i又被修改为了0;
    • 而i=++i的过程则是先对局部变量表中i的原始值进行加1的操作,即使得i由0变为1,然后将i的值复制到操作数栈,最后赋值即弹出操作数栈顶的值。
    • i++;和++i;的执行过程和结果是一样的。
    • 在使用i++和++i赋值的过程中,他们区别在于前者先复制当前数据,再进行原始值加1的操作,后者则先进行了原始值加1的操作,再对计算后的结果进行了复制,最后返回的其实都是放入操作数栈的拷贝。
    • 看懂了上面的原理,你应该能明白为什么int i = 0;i=i++ + ++i;等于2了吧。如果按原来的定义取理解,也许会得出结果为1。
    展开全文
  • 我:“i++是先把i的值拿出来使用,然后再对i+1,++i是先对i+1,然后再去使用i” 面试官:“那你看看下面这段代码,运行结果是什么?” public static void main(String[] args) { int j = 0; for (int i = 0; i .....

    面试官:你说你懂i++跟++i的区别,那你知道下面这段代码的运行结果吗?

    面试官:“说一说i++跟++i的区别”

    我:“i++是先把i的值拿出来使用,然后再对i+1,++i是先对i+1,然后再去使用i”

    面试官:“那你看看下面这段代码,运行结果是什么?”

    public static void main(String[] args) {
        int j = 0;
        for (int i = 0; i < 10; i++) {
            j = (j++);
        }
        System.out.println(j);
    }
    

    “以我多年的开发经验来看,它必然不会是10”

    面试官:

    在这里插入图片描述

    我:“哈哈…,开个玩笑,结果为0啦”

    面试官:“你能说说为什么吗?”

    我:“因为j++这个表达式每次返回的都是0,所以最终结果就是0”

    面试官:“小伙子不错,那你能从JVM的角度讲一讲为什么嘛?”

    我心想:这货明显是在搞事情啊,这么快就到JVM了?还好我有准备。

    首先我们知道,JVM的运行时数据区域是分为好几块的,具体分布如下图所示:
    在这里插入图片描述
    在这里插入图片描述
    现在我们主要关注其中的虚拟机栈,关于虚拟机栈,我们知道它有以下几个特点:

    1. Java虚拟机栈是线程私有的,它的生命周期和线程相同
    2. Java虚拟机栈是由一个个栈帧组成,线程在执行一个方法时,便会向栈中放入一个栈帧。
    3. 每一个方法所对应的栈帧又包含了以下几个部分
      • 局部变量表
      • 操作数栈
      • 方法出口

    那么现在虚拟机栈就可以表示成下面这个样子:

    在这里插入图片描述

    其中的局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

    局部变量表的最小存储单元为Slot(槽),其中64位长度的long和double类型的数据会占用2个Slot,其余的数据类型只占用1个。所以我们可以将局部变量表分为一个个的存储单元,每个存储单元有自己的下标位置,在对数据进行访问时可以直接通过下标来访问

    操作数栈对于数据的存储跟局部变量表是一样的,但是跟局部变量表不同的是,操作数栈对于数据的访问不是通过下标而是通过标准的栈操作来进行的(压入与弹出)之后在分析字节码指令时我们会很明显的感觉到这一点。另外还有,对于数据的计算是由CPU完成的,所以CPU在执行指令时每次会从操作数栈中弹出所需的操作数经过计算后再压入到操作数栈顶。

    以执行下面这段代码为例:

    public static  void main(String[] args){
        int a = 2;
        int b = 3;
        int c = a + b;
    }
    

    这个过程如下所示

    在这里插入图片描述

    这两步完成了局部变量a的赋值,同理b的赋值也一样,a,b完成赋值后此时的状态如下图所示

    在这里插入图片描述

    此时要执行a+b的运算了,所以首先要将需要的操作数加载到操作数栈,执行运算时再将操作数从栈中弹出,由CPU完成计算后再将结果压入到栈中,整个过程如下:

    在这里插入图片描述

    到这里还没有完哦,还剩最后一步,需要将计算后的结果赋值给c,也就是要将操作数栈的数据弹出并赋值给局部变量表中的第三个槽位

    在这里插入图片描述

    OK,到这一步整个过程就完成了

    面试官:“嗯,说的不错,但是你还是没解释为什么最开始的那个问题,为什么j=j++的结果会是0呢?”

    我:“面试官您好,要解释这个问题上面的知识都是基础,真正要说明白这个问题我们需要从字节码入手。”

    我们进入到这段代码编译好的.class文件目录下执行:javap -c xxx.class,得到其字节码如下:

    // 为方便阅读将对应代码也放到这里
    public static void main(String[] args) {
        int j = 0;
        for (int i = 0; i < 10; i++) {
            j = (j++);
        }
        System.out.println(j);
    }
    
      public static void main(java.lang.String[]);
        Code:
           0: iconst_0    // 将常数0压入到操作数栈顶
           1: istore_1    // 将操作数栈顶元素弹出并压入到局部变量表中1号槽位,也就是j=0
           2: iconst_0    // 将常数0压入到操作数栈顶
           3: istore_2	  // 将操作数栈顶元素弹出并压入到局部变量表中2号槽位,也就是i=0
           4: iload_2     // 将2号槽位的元素压入操作数栈顶
           5: bipush        10   // 将常数10压入到操作数栈顶,此时操作数栈中有两个数(常数10,以及i)
           7: if_icmpge     21	 // 比较操作数栈中的两个数,如果i>=10,跳转到第21行
          10: iload_1			 // 将局部变量表中的1号槽位的元素压入到操作数栈顶,就是将j=0压入操作数栈顶
          11: iinc          1, 1 // 将局部变量表中的1号元素自增1,此时局部变量表中的j=1
    
          14: istore_1			 // 将操作数栈顶的元素(此时栈顶元素为0)弹出并赋值给局部变量表中的1号							      槽位(一号槽位本来已经完成自增了,但是又被赋值成了0)
          
          15: iinc          2, 1 // 将局部变量表中的2号槽位的元素自增1,此时局部变量表中的2号元素值为1,也就是i=1
          
          18: goto          4	 // 第一次循环结束,跳转到第四行继续循环
          21: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
          24: iload_1
          25: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
          28: return
    
    

    我们着重关注第10,11,14行字节码指令,用图表示如下:

    在这里插入图片描述

    可以看到本来局部变量表中的j已经完成了自增(iinc指令是直接对局部变量进行自增),但是在进行赋值时是将操作数栈中的数据弹出,但是操作数栈的数据并没有经过计算,所以每次自增的结果都被覆盖了。最终结果就是0。

    我们平常说的i++是先拿去用,然后再自增,而++i是先自增再拿去用。这个到底怎么理解呢?如果站在JVM的层次来讲的话,应该这样说:

    1. i++是先被操作数栈拿去用了(先执行的load指令),然后再在局部变量表中完成了自增,但是操作数栈中还是自增前的值
    2. 而++1是先在局部变量表中完成了自增(先执行innc指令),然后再被load进了操作数栈,所以操作数栈中保存的是自增后的值

    这就是它们的根本区别。

    最后我这里放出一段代码及其字节码,我相信看完这篇文章你对于i++及++i的理解绝对跟原来不一样了

    public static void main(String[] args) {
        int i = 4;
        int b = i++;
        int a = ++i;
    }
    
    public static void main(java.lang.String[]);
    Code:
    	   0: iconst_4
           1: istore_1
           2: iload_1
           3: iinc          1, 1
               6: istore_2
           7: iinc          1, 1
               10: iload_1
          11: istore_3
          12: return
    

    这段代码大家自行思考,有任何问题可以给我留言哦~

    码字不易,记得点个赞哈~

    ps:

    图中局部变量表的下标都是从1开始,这是因为我直接用main函数测试的,局部变量表中下标为0的元素是main函数中的形参,也就是String[]args。另外也通过这些过程我们也可以发现,局部变量表就是通过下标访问的,而操作数栈就是通过正常的栈操作(压入/弹出)来完成数据访问的

    展开全文
  • 终于弄明白 i = i++和 i = ++i

    万次阅读 多人点赞 2020-06-09 15:54:57
    写在前面:前些天看完了JVM的内存结构,自以为自己是懂了,心里想想不就是分线程共享和...文章目录1、题目2、分析2.1、第一步2.2、第二步2.3、第三步2.4、第四步2.5、结果3、i = ++i 1、题目 package pers.mobian..

    写在前面:前些天看完了JVM的内存结构,自以为自己是懂了,心里想想不就是分线程共享和线程私有嘛,然后又怎么怎么分怎么怎么的嘛…


    直到遇到了这道题目。说句实话,曾经自己做这种运算题目,完全是靠脑子空想,然后拿上笔颤抖的写下一个自己都不知道正不正确的答案。不过过了今天,我终于能确定它的答案了。


    为此,我也专门写一篇博客,记录我的学习!!!




    1、题目

    package pers.mobian.questions01;
    
    public class test01 {
        public static void main(String[] args) {
            int i = 1;
            i = i++;
            int j = i++;
            int k = i + ++i * i++;
            System.out.println("i="+i);
            System.out.println("j="+j);
            System.out.println("k="+k);
        }
    }
    

    你能肯定并且准确的说出你的答案吗?





    2、分析

    在做这道题时我们的脑中要有对应的JVM内存结构。其中一个方法对应一个栈帧

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7S1O4RTM-1591687148745)(01_自增变量.assets/image-20200607101447244.png)]

    此题目我们只需要用到栈帧里面的局部变量表和操作数栈


    2.1、第一步

    int i = 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2xGca3Uz-1591687148747)(01_自增变量.assets/image-20200607103756406.png)]

    只是一个简单的赋值操作


    2.2、第二步

    i = i++

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bLH5OxNN-1591687148749)(01_自增变量.assets/image-20200607104123111.png)]

    结果:i还是等于1


    2.3、第三步

    int j = i++

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6LTb9Ge-1591687148751)(01_自增变量.assets/image-20200607104936003.png)]

    结果:i在局部变量表中变成了2,操作数栈中的 i 值为1,并且将 i 的值返回给 j,即此条语句以后,i = 2,j = 1


    2.4、第四步

    int k = i + ++i * i++

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EOsOaSzt-1591687148753)(01_自增变量.assets/image-20200607110626891.png)]

    结果:局部变量表中的i = 4,k = 11


    2.5、结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6eruEoFm-1591687148754)(01_自增变量.assets/image-20200607110650949.png)]




    3、i = ++i

    按理说根据上面的分析过程,再来分析 i = ++i,就很简单了。
    我们的 i 变量先在局部变量表中进行自增,然后再将 i 进栈,然后再把栈中的数据返回给我们的变量 i 。

    public class test02 {
        public static void main(String[] args) {
            int i = 1;
            i = ++i;
            System.out.println(i); // 结果:i = 2
        }
    }
    


    最后的最后:当我和室友讨论一番之后,发现C语言的计算机制和Java的有些许出入,所以本文的思考过程和计算推导仅针对Java语言

    展开全文
  • 一看就懂的i++和++i详解

    万次阅读 多人点赞 2020-05-09 22:10:35
    转载请说明出处,本文来自Android菜鸟:http://blog.csdn.net/android_cai_niao/article/details/42490473 QQ:2717521606 ...System.out.println("i = " + i); 示例2 int a = 2; int b = (3 * a++) + a;

    前言

    转载请说明出处,本文来自Android菜鸟:https://blog.csdn.net/android_cai_niao/article/details/106027313 QQ:2717521606

    我相信很多朋友可能之前已经百度过i++和++i的相关文章了,也做过不少的练习,觉得自己已经深刻掌握了它们之间的原理了,真的是这样的吗?来试试计算一下我下面提供的几道练习,你就会发现你又不会了!

    示例代码

    请先自己心算一下答案,然后找个本子记下来,然后再跟我后面的答案对比,看你能做对几道题,能做对两题以上的我喊你大哥!!

    示例1

    int i = 0;
    i = i++; 
    System.out.println("i = " + i); 
    

    示例2

    int a = 2; 
    int b = (3 * a++) + a;
    System.out.println(b);
    

    示例3

    int a = 2; 
    int b = a + (3 * a++);
    System.out.println(b);
    

    示例4

    int i = 1;
    int j = 1;
    int k = i++ + ++i + ++j + j++; 
    System.out.println(k);
    

    示例5

    int a = 0;
    int b = 0;
    a = a++;
    b = a++;
    System.out.println("a = " + a + ", b = " + b);
    

    示例答案

    示例1:0
    示例2:9
    示例3:8
    示例4:8
    示例5:a = 1, b = 0

    i++ 和 ++i原理

    i++ 即后加加,原理是:先自增,然后返回自增之前的值
    ++i 即前加加,原理是:先自增,然后返回自增之后的值
    重点:这是一般人所不知道的,记住:不论是前++还是后++,都有个共同点是先自增。
    对于++i 就不说了,大多数人都懂,而对于 i++ 的原理,我用代码模拟其原理,如下:

    int temp = i;
    i = i + 1;
    return temp;  
    

    这3句代码就是上面所说的那样:i++是先自增,然后才返回自增之前的值。

    i++字节码分析

    有很多的人写的文章上都是说i++是先返回i的值,然后再自增,这是错误,是先自增,然后再返回自增之前的值,你可能会问,这有区别吗?答案:有的。只要这个没理解对,则你在计算i++的相关问题时就有可能算错。

    有的人可能又会问了,我凭什么相信你,你有什么证据证明i++是先自增,然后再返回自增之前的值吗?我还真去找过证据,我们把class的字节码搞出来,分析一下就知道了,证明如下:

    public class Test {
        void fun() {
            int i = 0;
            i = i++;
        }
    }
    
    

    如上,我们写了一个超级简单的Test类。在cmd中输入这个命令(javap -c Test.class)以查看其生成的字节码,截图如下:
    i++字节码图
    我们关注fun()方法这一段就可以了,如下:
    i++字节码图

    这就是fun()函数对应的字节码了,我们一行一行的来分析,首先我们要说两个概念,一个是变量,一个是操作栈,fun()方法中有两个变量,哎,不是只有一个变量i吗?怎么会有两个?要了解这个你需要去学习字节码的相关知识,这里我们不深究,我画图如下:
    在这里插入图片描述
    如上图,变量有两个,在位置0的变量是什么我们不要管,系统自动分配的,你要知道的是位置1的变量其实就是我们定义的变量i就行了,接下来,我们来一行行分析fun()方法对应的字节码:
    “ iconst_0 ” i代表int类型,const代表常量,0就代表整数0,整句话的意思就是把int类型的常量0放入操作栈的栈顶中,图解如下:
    在这里插入图片描述
    “ istore_1 ” i代表int类型,store代表存储,1代表位置为1的变量,整句话的意思就是把操作栈中栈顶的值拿走,保存到位置为1的变量上,图解如下:
    在这里插入图片描述
    “ iload_1 ” i代表int类型,load代表加载变量的值,1代表位置为1的变量,整句话的意思就是把位置为1的变量的值加载到操作栈的栈顶中,图解如下:
    在这里插入图片描述
    “ iinc 1, 1 ” i代表int类型,inc(increment)代表增加,这里还有两个1,前面的1代表对位置为1的变量,第2个1代表增加1,因为有i += 3这种自增操作,这种情况的话第2个数字会是3,即自增3(iinc 1, 3)。“iinc 1, 1” 整句话的意思就是把位置为1的变量的值增加1,图解如下:
    在这里插入图片描述
    注:自增操作不会改变操作栈中的值,所以变量i的值自增后变成了1,而操作栈中的值还是0。

    “ istore_1 ” i代表int类型,store代表存储,1代表位置1的变量,整句话的意思就是:把栈顶中的值拿走,保存到位置为1的变量中,图解如下:
    在这里插入图片描述
    所以,这几行字节码合起来看,i++不就是先自增,然后才返回自增之前的值嘛!!所以大家千万别搞错顺序了。 用代码理解的话,就相当于下面的代码:

    int temp = i;
    i = i + 1;
    return temp;  
    

    最后再把++i的字节码图也贴一下,大家可以根据我上面讲解的知识分析一下,就会知道++i和i++的区别了:

    void fun() {
        int i = 0;
        i = ++i;
    }
    

    ++i字节码图

    表达式原则

    表达式有一个原则:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

    来看一下 if 语句的其中一种结构定义:

    if (条件表达式) 语句;
    

    用这个结构写个代码,如下:

    boolean b = true;
    int i = 0;
    if(b) i++;
    

    按照上面 if 语句的结构定义,if括号中是一个表达式,但是上面代码写了一个变量b进去,这是一个变量啊,怎么也能当成一个表达式么?没错,一个变量也是表达式。

    记住这个重点:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

    讲到这里,估计有人会对这个运算顺序和乘法这些搞混了,示例如下:

    int a = 0;
    int b = a + a * 2;
    

    如上代码,按着我的说法,一个变量也是一个表达式,“b = a + a * 2”这里a出现了两次,就是有两个a表达式,从左到右算的话先算a + a,这肯定不对啊,这不是我的意思哈,乘除法的优先级还是不能乱的,那应该先算a * 2吗?也不对,应该是这样的:因为有乘法,所以a * 2优先组成表达式,而不是a + a组成表达式,也就是说总体上可以分为两个表达式:“a” 表达式 和 “a * 2” 表达式,这两个表达式相加肯定从左到右计算嘛,先算完a表达式的结果,再算a * 2表达式的结果。你可能会想先算a和先算a * 2有区别吗?答案是:有的,看完下面 的“示例3详解” 你就清楚了。

    示例答案详解

    示例1详解

    int i = 0;
    i = i++;  
    System.out.println("i = " + i);  // 结果:0
    

    先看i++,根据原理“先自增,然后返回自增之前的值”,i 自增后,i = 1,但是接着返回自增之前的值0,此时表达式变成 i = 0,0没有赋值给 i 时 i 的值是1,但是当把0赋值给 i 时,i 的值就又变成0了。因此 i = i++ 这句代码是做无用功,因为 i 的值最终还是和原来一样。

    示例2详解

    int a = 2; 
    int b = (3 * a++) + a;
    System.out.println(b);   // 结果:9
    

    int b = (3 * a++) + a;a++后,a = 3,并返回自增之前的值2,所以此时表达式为:

    int b = (3 * 2) + a;此时a的值已经是3了,表达式又变为:

    int b = (3 * 2) + 3; 所以b = 9

    示例3详解

    int a = 2; 
    int b = a + (3 * a++);
    System.out.println(b); // 结果:8
    

    这题和示例2几乎一样啊,只是换了一下顺序而已,为什么结果就不一样了呢?这就需要用到“表达式原则 了”:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

    int b = a + (3 * a++);按一般人的想法是先算 3 * a++,a 先自增 a=3,然后返回自增之前的值2,所以此时表达式变为:

    int b = a + (3 * 2); 此时a的值为3了,表达式又变为:

    int b = 3 + (3 * 2);结果 b = 9

    我们说一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的,这个理论你可能不能深刻体会,但是如果我把代码稍微改一下你就能理解了,如下:

    int b = (a * 1) + (3 * a++) 这个代码和 int b = a + (3 * a++) 是一样的,没有区别,但是看(a *1)你就很容易的知道要先算a * 1这个表达式,表达式的结果为2。

    所以,虽然 int b = a + (3 * a++) 中前面的a只是一个变量,但他也是一个表达式,a这个表达式和(3 * a++)这个表达式进行相加,多个表达式的运算都是从左到右进行的,所以先算a这个表达式,a表达式计算结果为2,所以表达式变成:

    int b = 2 + (3 * a++) 然后是a自增并返回自增之前的值2,所以表达式又变为:

    int b = 2 + (3 * 2);所以结果为8。此时a的值为3

    示例4详解

    int i = 1;
    int j = 1;
    int k = i++ + ++i + ++j + j++;  
    System.out.println(k);  // 结果:8
    

    有了前面3条示例的详解,相信这一条大家就能自己解答了,可以先自己解答一下,看结果是不是8,不是的话,再来看我下面的讲解:

    表达式原则说多个表达式的加减法运算都是从左到右进行的,这里的表达式有:i++、++i、++j、j++,都是加法,那我们就从左到右计算这4个表达式就OK了,如下:

    1、先算i++,i++之后i的值为2,并返回++之前的值1,所以整个表达式可以变为:

       1 + ++i + ++j + j++; // 此时的i值为2

    2、再计算++i,++i之后i的值为3,并返回3,所以整个表达式可以变为:

       1 + 3 + ++j + j++; // 此时i的值为3

    3、再计算++j,++j之后j的值为2,并返回2,所以整个表达式可以变为:

       1 + 3 + 2 + j++; // 此时j的值为2

    4、再计算j++,j++之后 j的值为3,并返回2,所以整个表达式可以变为:

       1 + 3 + 2 +2; // 结果为8,此时j的值为3

    示例5详解

    int a = 0;
    int b = 0;
    a = a++;
    b = a++;
    System.out.println("a = " + a + ", b = " + b); // a = 1, b = 0
    

    到了第5题,好像已经没有难度了,大家应该都能解出来了,但是为了文章的完整性,我还是分解一下,大家应该自己先算一次,算不对再来看我的分解:

    a = a++; a++之后a的值为1,并返回0,所以a的值由1又变回了0
    b = a++; a++之后a的值为1,并返回0,0赋值给b,所以b为0,而a还是1哦!!

    总结

    • i++ 即后加加,原理是:先自增,然后返回自增之前的值
    • ++i 即前加加,原理是:先自增,然后返回自增之后的值
    • 一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的
    • 真实开发中,我们不会写这些复杂的i++代码,但是为什么还要掌握这些细节呢?答:笔试,万一笔试的时候遇到这样的题目呢?回答对了就可以加分了,因为这种题很多人是答不出来的,而你回答出来了,那可是很加分的哦!
    展开全文
  • 【Python】for i in range ()作用

    万次阅读 多人点赞 2018-02-02 13:47:23
    for i in range ()作用: range()是一个函数, for i in range () 就是给i赋值: 比如 for i in range (1,3): 就是把1,2依次赋值给i range () 函数的使用是这样的: range(3)即:从1到3,不包含3,即1,2 ...
  • 说到i5-10400F就有必要提一提它的前辈i5-9400F,最大的特点是F后缀不带核显,但是价格更划算。所以不要这个核显反而对于玩家来说是一大优势,因为大部分用户既然已经选择到了酷睿i5系列,一般都是会搭配独立显卡一起...
  • Java中i++和++i的区别

    万次阅读 多人点赞 2017-01-18 11:41:42
    说来惭愧,从事开发工作也有一年时间了,然而在今天的一个业务逻辑里突然发现原来我对i++和++i都没有理解,或者说我之前的理解是错误的。这对于一个有追求的程序猿是不能容忍的。知道之后,迅速恶补学习,现在算是...
  • C语言i++和++i的区别

    万次阅读 多人点赞 2016-12-03 11:09:28
    i++和++i的区别虽然简单,还是记录一下吧!   【知识点】  1. 对于普通独立的语句,i++和++i是一样的,如:  i++; 等效于i=i+1;  ++i; 等效于i=i+1;  2. 用于赋值语句,i++先赋值后加1,++i先加1后赋值,如: ...
  • i++ 、 ++ii+=1、i=i+1 的问题

    万次阅读 多人点赞 2019-04-19 22:36:40
    i++ 、 ++ii+=1、i=i+1 等这类问题是也笔试必考内容之一,基础中的基础。如果不会,那就已经可以看出你个人的基础水平了。虽然看上去简单,但是不懂原理是不行的。只有了解原理才能记忆最深 i++ 和 ++i 的区别: ...
  • for循环中i++与++i的区别

    万次阅读 多人点赞 2019-03-17 09:29:26
    1、++i是先改变i的值即加1后再使用i的值;而i++是先使用i的值在改变它的值即加。 2、for循环内部仅形式不同:当i++循环和++i循环在for循环内部,虽然形式上明显不同,但输出结果可以一样。如下图所示: for(i++)...
  • int i=0, i=i++ 和i++ 区别

    千次阅读 2019-06-11 16:18:00
    int i=1; i=i++; System.out .print(i); 输出1 a=i++在我的理解是这样的,它先给i赋一个临时变量比如a,即a=i=1, 然后运算 i=i++, 在最后运算的时候 i 的值使用的是被赋值的临时变量a的值,并非变量本身 即 先...
  • 今天在安装OpenCV时,报了如下错误: 笔者在网上查找一番后...boostdesc_bgm.i boostdesc_bgm_bi.i boostdesc_bgm_hd.i boostdesc_lbgm.i boostdesc_binboost_064.i boostdesc_binboost_128.i boostdesc_binboos...
  • i310100f还是i510400f这些点很重要!看完你就知道了 https://bijiben.jd.com/list.html? i5-10400F的基础频率为2.9GHz,最大睿频可达4.3GHz,对比去年的i5-9400F睿频频率增加了0.2GHz,TDP只有65W,并且超线程的...
  • i++和++i有什么区别? 1.i++是先赋值,然后再自增;++i是先自增,后赋值。 2.i=0,i++=0,++i=1; Console.WriteLine(++i==i++); 结果位true
  • i510200h和i79750h哪个好

    万次阅读 2020-09-09 11:28:22
    i5-10200H的酷睿处理器,它...选i5 10200h还是i79750h这些点很重要!看完你就知道了 https://list.jd.com/list.html? i7-9750H基础频率bai2.6GHz,最大睿频4.5GHz,i7-9750H的基础频率、最大睿频都比dui7-8750H高了40
  • i510200h和i78750h哪个好

    万次阅读 2020-09-09 11:34:58
    选i5 10200h还是i78750h这些点很重要!看完你就知道了 https://list.jd.com/list.html? 英特尔酷睿i7 8750H处理器是目前高性能游戏本主要配置的硬件核心,升级六核心之后为游戏本带来了极为强劲的性能提升。检测显示...
  • i=i++深入解释

    千次阅读 多人点赞 2019-07-31 21:57:55
    以下内容是在JAVA虚拟机中探究,学习...i初始化位0,题目中是i++,所以先进行i=ii=0; 然后进行自加操作:i=1; 看似完美的步骤,其实是错的,难受哦。。。 那就往他祖坟上刨一下子: int是基本类型,存在于栈中...
  • python i+=1与i=i+1的区别

    千次阅读 2019-08-27 18:14:14
    探索 i += 1 与 i = i+1 的区别 ''' ''' 对于不可变对象int str tuple 都会创建一个新的内存地址 ''' i = 0 print(id(i)) # 1564271360 i += 1 print(id(i)) # 1564271376 i = i + 1 print(id(i)) # 1564271392 '...
  • i=i+1 与 i+=1 区别

    千次阅读 2019-03-19 09:34:41
    满脑子的黑人问号有没有,有的话点一波关注:) 起初我也是一脸蒙逼,随着知识的积累,有了新的思考,记录下来仅供参考。 从执行效率上来说,i+=1更优 我们先来看一段代码 public class PlusEqualTest {... i=i...
  • 我都知道怎么说这问题,一个桌面处理器 一个笔记本处理器如果让我选,我选i710750h系列笔记本 方便 2020年最新主流台式机组装电脑配置推荐(中高低配置详细推荐) https://diannao.com/ts 2020年最佳笔记本电脑是谁?...
  • i710875h和i710750h哪个好

    万次阅读 2020-09-07 18:22:10
    i710875h和i710750h相比来说,i710875h会更加好一点。 选i710875h还是i710750h哪个好 这些点很重要!看完你就知道了 https://list.jd.com/list.html? i710875h为八核心十六线程,而i710750h则为六核十二线;i710750h...
  • Java中i++,++ii= i+ 1的区别

    千次阅读 2017-08-29 10:20:37
    Java中i++,++i都表示 i = i+1 i++是先是使用 i的原值,然后再原值的基础上加1. ++i是先在原值的基础上加1,然后在使用i的值。 直接看一个示例: public class Test1 { public static void main(String [] ...
  • i2cdetect i2cdump i2cget i2cset用法

    万次阅读 多人点赞 2016-12-14 11:58:27
    本博客转载自台湾朋友的文章:...幸好linux上也有這樣的工具 – i2c tools。先到lm-sensors下載soure code,然後cross compile成arm的執行檔,就可以放到板子來試試看了。 i2c-tools中含有四個執行檔i2cdetect –
  • java i=i+2与i+=2的区别

    2017-09-16 07:19:25
    i=i+2; j--; } else if(i)//行越界 { i=N-1; } else if(j>=N) //列越界 { j=0; } else if(a[i][j]!=0) //有冲突 { i=i+2; j--; } a[i][j]=n; } ...
  • i++和++i的深刻理解

    万次阅读 多人点赞 2019-05-17 22:44:51
    前言:初学C语言的朋友一定对i++和++i傻傻分不清楚,本文将清楚的阐述两者的区别与联系。 话不多说我们先来看一下例子: #include <stdio.h> int main() { int i = 0,j; i++; printf("单独存在的i++...
  • i510400和i510400F有什么区别?

    万次阅读 2020-08-22 17:58:22
    i510400和i510400F哪一款值得购买?懂电脑人这样选! https://diannao.jd.com 2、十代I5 10400F:十代I5 10400F的CPU主频为3.1GHz,动态加速频率为4.4GHz。 二、热设计功耗(TDP)不同 1、十代I5 10400:十代I5 10400的...
  • i510400和i710700 哪个好

    千次阅读 2020-09-02 09:27:06
    i510400还是i710700哪个好 这些点很重要!看完你就知道了 https://3c.tmall.com/? HT超线程,i7有超线程技术,i5没有。实际使用的情况下,两者的性能差距很微弱,因为ht的i7虽有8个线程,但这跟真正的8核心差距还是...
  • docker run 的 -i -t -d参数

    万次阅读 多人点赞 2019-10-28 15:17:23
    docker run 的 -i -t -d前言这三个参数(-i, -t, -d)是啥意思该如何组合应用这三个参数其他参数 前言 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何...
  • java i=i++和j=i++的区别

    万次阅读 2018-04-27 13:52:00
    i=i++;j=i++的区别 i=i++-----------在java中 这个语句的前后顺序应该是这样的(tmp=i;i++;tmp==i) java的编译器在遇到i++和i- -的时候会重新为变量运算分配一块内存空间,以存放原始的值,而在完成了赋值运算之后...
  • java中i++ 和 ++i的区别

    万次阅读 多人点赞 2018-06-20 17:27:28
    很多人很清晰的知道这两道题想考察面试者对 i++ 和 ++i 的理解,也很清晰的知道这二者的区别,但是题还是做不对;两道题如下,大家可以先思考一下,给个答案,然后再去验证自己的想法。 (这是一道典型的看着非常...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,512,311
精华内容 2,204,924
关键字:

i