前置++和后置++ 运算的详解及实例代码

一般认为前置++是先将变量的值加1,然后使用加1后的值参与运算;而后置++是先使用该值参与运算,然后再将该值加1。

先看第一个例子:

package test;
public class Plus_Test01 {
 public static void main(String[] args) {
  int i = 100;
  i = i++;
  System.out.println(i);
 }
}

猜猜结果是什么?

接着看第二个:

package test;
public class Plus_Test02 {
 public static void main(String[] args) {
  int k = 100;
  while (true) {
   if (k++ > 100) {
    // System.out.println(k);
    break;
   }
   System.out.println(k);
  }
 }
}

猜猜结果是什么?

实际上,不管是前置++,还是后置++,都是先将变量的值加1,然后才继续计算的。二者之间真正的区别是:前置++是将变量的值加1后,使用增值后的变量进行运算的,而后置++是首先将变量赋值给一个临时变量,接下来对变量的值加1,然后使用那个临时变量进行运算。

对于如下代码片段(前置++):

int i=1;
int j=++i*5;

实际第二句上相当于:

i+=1; //将i加1
j=i*5; //将加1后的值与之进行计算, 此结果为:10

而对于如下代码片段(后置++):

int i=1;
int j=i++*5;

第二句上相当于:

int temp=i;  // 将i赋值给一个临时变量
i+=1;        //将i加1
j=temp*5;   //将临时变量与之计算, 此结果为:5

对于第一个例子,相当于:

int temp=i;
i+=1;
i=temp; //

所以结果应该为不变的,即100。

第一个例子的汇编代码为:

 public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
  stack=2, locals=2, args_size=1
   0: bipush  100
   2: istore_1
   3: iload_1
   4: iinc   1, 1 //local var中第二个 加1
   7: istore_1    //保存至local var
   8: getstatic  #16     // Field java/lang/System.out:Ljava/io/PrintStream;
   11: iload_1 //加载的参数为栈中的第二个,即仍然为100
   12: invokevirtual #22     // Method java/io/PrintStream.println:(I)V
   15: return

对于第二个例子,其实不难,结果是101,注意看一下流程,以后不能在犯这样的错误了。(流程为:首先比较temp=i,temp>100,,显然不成立,将i+=1,跳到syso那一句,打印的当然是101,再次循环同样有temp=i,temp>100,这次是成立的,然后i+=1,直接跳出循环,不会执行while里面的语句)。

第二个例子的汇编(只选取了main方法):

 public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
  stack=2, locals=2, args_size=1
   0: bipush  100  //100压栈
   2: istore_1     //保存至第二个local var(第一个local var 是方法参数)
   3: iload_1     //从第二个local var加载
   4: iinc   1, 1  //给local var的2号位置的int值增加1(局部变量自增,结果仍然在local var中,操作数栈顶1不会变)
   7: bipush  100 //100压栈
   9: if_icmple  15 //比较操作数栈顶的两个int整型值,如果第一个小于或者等于第二个的话,然后跳转到15行
   12: goto   25 //否则跳转到25行(即操作数栈顶1>操作数栈顶2)
   15: getstatic  #2     // Field java/lang/System.out:Ljava/io/PrintStream;
   18: iload_1 // //从第一个个local var加载
   19: invokevirtual #3     // Method java/io/PrintStream.println:(I)V //调用该方法
   22: goto   3 //再次回跳至3,再次循环
   25: return //退出

第三个例子:

 package test;

 public class Plus_Test03 {

  static int proPlus() {
   int i = 55;
   int j = ++i;
   return j; //56
  }

  static int postPlus() {
   int i = 55;
   int j = i++;
   return j; //55
  }

  public static void main(String[] args) {
  System.out.println(proPlus());//56
   System.out.println(postPlus());//55

  }
}

第三个例子的汇编:

static int proPlus();
 descriptor: ()I
 flags: ACC_STATIC
 Code:
  stack=1, locals=2, args_size=0
   0: bipush  55 //55压栈
   2: istore_0   //将int型栈顶的存储至第一个local var
   3: iinc   0, 1 //第一个local var加1
   6: iload_0   //从local var加载
   7: istore_1  //保存至第二个local var
   8: iload_1   //栈顶为第二个local var
   9: ireturnstatic int postPlus();
 descriptor: ()I
 flags: ACC_STATIC
 Code:
  stack=1, locals=2, args_size=0
   0: bipush  55
   2: istore_0
   3: iload_0    //加载至栈
   4: iinc   0, 1 //第一个local var加1
   7: istore_1
   8: iload_1
   9: ireturn

可见,前置++ 和后置++的不同点在于上面蓝色(//第一个local var加1)的部分,这两部分是反过来的。对于前置来说,会将local var中的数加1然后加载至栈中,而后置则是先从栈local var中加载至栈,然后将local var的加1,相当于留了一个备份。

结论:

一。前置、与后置++都是先将变量的值加1,而不是前置++先加1然后运算,而后置++先运算后加1。
二。从程序上说,后置++先将变量赋值给一个临时变量,然后将变量的值加1,接下来使用那个临时变量参与运算。
三。从指令上说,后置++在执行增值指令(iinc)前,先将变量的值压入栈,执行增值指令后,使用的是之前压入栈的值。

希望通过此文,彻底理解前置++和后置++的运算区别,谢谢大家对本站的支持!

(0)

相关推荐

  • golang简单位运算示例

    本文实例讲述了golang简单位运算.分享给大家供大家参考,具体如下: 复制代码 代码如下: // http://play.golang.org/p/idG7Ri_krU package main import "fmt" func main() {     var n uint8 = 6     fmt.Printf("%s\n%08b\n%08b\n\n", "6<<1 左移1位", n, n<<1)     fmt.

  • C语言关系运算符实例详解

    在程序中经常需要比较两个数据的大小,以决定程序下一步的工作.比如一个程序限制了只能成年人使用,儿童因为年龄不够,没有权限使用.这时候程序就需要获取用户输入的年龄并做出判断,如果超过18岁就正常运行,否则给出无权使用的提示. 比较两个数据大小的运算符称为关系运算符(Relational Operators). 在C语言中有以下关系运算符: 1) <(小于) 2) <=(小于或等于) 3) >(大于) 4) >=(大于或等于) 5) ==(等于) 6) !=(不等于) 关系运算符都是双

  • C++实现 vector 的四则运算

    这里假设 vector 的运算定义为对操作数 vector 中相同位置的元素进行运算,最后得到一个新的 vector.具体来说就是,假如 vector<int> d1{1, 2, 3}, d2{4, 5, 6};则, v1 + v2 等于 {5, 7, 9}.实现这样的运算看起来并不是很难,一个非常直观的做法如下所示: vector<int> operator+(const vector<int>& v1, const vector<int>&

  • C 语言条件运算符详细讲解

    如果希望获得两个数中最大的一个,可以使用 if 语句,例如: if(a>b){ max = a; }else{ max = b; } 不过,C语言提供了一种更加简单的方法,叫做条件运算符,语法格式为: 表达式1 ? 表达式2 : 表达式3 条件运算符是C语言中唯一的一个三目运算符,其求值规则为:如果表达式1的值为真,则以表达式2 的值作为整个条件表达式的值,否则以表达式3的值作为整个条件表达式的值.条件表达式通常用于赋值语句之中. 上面的 if else 语句等价于: max = (a>b)

  • 教你JS中的运算符乘方、开方及变量格式转换

    1)如何计算乘方 题一:3的4次方(不会打,请原谅 ==!!!) 3的4次方=3*3*3*3 var a = Math.pow(3,4); console.log(a); 说明:Math.pow()是用来计算乘方的语法 注意:Math的M是大写: 题二:3的4*5次方 var a =Math.pow(3,4*5); console.log(a); 2)如何计算根号 题目:根号81 var a = Math.sqrt(81); console.log(a); 变量格式转换 用户的输入 var ye

  • C语言 运算符详细介绍及示例代码

    C 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号.C 语言内置了丰富的运算符,并提供了以下类型的运算符: 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 本章将逐一介绍算术运算符.关系运算符.逻辑运算符.位运算符.赋值运算符和其他运算符. 算术运算符 下表显示了 C 语言支持的所有算术运算符.假设变量 A 的值为 10,变量 B 的值为 20,则: 运算符 描述 实例 + 把两个操作数相加 A + B 将得到 30 - 从第一个操作数中减去第二个操作数 A

  • 浅谈JS运算符&&和|| 及其优先级

    今天看了一段YUI compressor压缩的js代码: userNum && (ind += index,ind >= userNum && (ind -= userNum),ind < 0 && (ind === -2 && (ind = -1),ind += userNum),selLi.removeClass("on"),$(selLi[ind]).addClass("on"));

  • C语言逻辑运算符知识整理

    在高中数学中,我们学过逻辑运算,例如p为真命题,q就假命题,那么"p且q"为假,"p或q"为真,"非q"为真. 在C语言中,也有类似的逻辑运算: 运算符 说明 结合性 举例 && 与运算,双目,对应数学中的"且" 左结合 1&&0.(9>3)&&(b>a) || 或运算,双目 左结合 1||0.(9>3)||(b>a) ! 非运算.单目 右结合 !a.!(

  • JS中的==运算: [''] == false —>true

    计算下面表达式的值: [''] == false 首先,两个操作数分别是对象类型.布尔类型.根据图1,需要将布尔类型转为数字类型,而false转为数字的结果是0,所以表达式变为: [''] == 0 两个操作数变成了对象类型.数字类型.根据图1,需要将对象类型转为原始类型: 首先调用[].valueOf(),由于数组的valueOf()方法返回自身,所以结果不是原始类型,继续调用[].toString(). 对于数组来说,toString()方法的算法,是将每个元素都转为字符串类型,然后用逗号'

  • 前置++和后置++ 运算的详解及实例代码

    一般认为前置++是先将变量的值加1,然后使用加1后的值参与运算:而后置++是先使用该值参与运算,然后再将该值加1. 先看第一个例子: package test; public class Plus_Test01 { public static void main(String[] args) { int i = 100; i = i++; System.out.println(i); } } 猜猜结果是什么? 接着看第二个: package test; public class Plus_Tes

  • java多线程编程技术详解和实例代码

     java多线程编程技术详解和实例代码 1.   Java和他的API都可以使用并发. 可以指定程序包含不同的执行线程,每个线程都具有自己的方法调用堆栈和程序计数器,使得线程在与其他线程并发地执行能够共享程序范围内的资源,比如共享内存,这种能力被称为多线程编程(multithreading),在核心的C和C++语言中并不具备这种能力,尽管他们影响了JAVA的设计. 2.   线程的生命周期 新线程的生命周期从"新生"状态开始.程序启动线程前,线程一直是"新生"状态:

  • MySQL 序列 AUTO_INCREMENT详解及实例代码

    MySQL 序列 AUTO_INCREMENT详解及实例代码 MySQL序列是一组整数:1, 2, 3, ...,由于一张数据表只能有一个字段自增主键, 如果你想实现其他字段也实现自动增加,就可以使用MySQL序列来实现. 本章我们将介绍如何使用MySQL的序列. 使用AUTO_INCREMENT MySQL中最简单使用序列的方法就是使用 MySQL AUTO_INCREMENT 来定义列. 实例 以下实例中创建了数据表insect, insect中id无需指定值可实现自动增长. mysql>

  • Java 两种延时thread和timer详解及实例代码

    Java 两种延时thread和timer详解及实例代码 在Java中有时候需要使程序暂停一点时间,称为延时.普通延时用Thread.sleep(int)方法,这很简单.它将当前线程挂起指定的毫秒数.如 try { Thread.currentThread().sleep(1000);//毫秒 } catch(Exception e){} 在这里需要解释一下线程沉睡的时间.sleep()方法并不能够让程序"严格"的沉睡指定的时间.例如当使用5000作为sleep()方法的参数时,线 程

  • Spring AOP 基于注解详解及实例代码

    Spring AOP  基于注解详解及实例代码 1.启用spring对@AspectJ注解的支持: <beans xmlns:aop="http://www.springframework.org/schema/aop"...> <!--启动支持--> <aop:aspectj-autoproxy /> </beans> 也可以配置AnnotationAwareAspectJAutoProxyCreator Bean来启动Spring对@

  • JS 实现计算器详解及实例代码(一)

    Javascript 实现计算器: 系列文章: JS 实现计算器详解及实例代码(一) Javascript 实现计算器时间功能详解及实例(二) 小型JavaScript计算器 自己寻思出的解决方案,比较笨拙的方法,虽然完成了但是还有不少bug,用的方法也不是最有效的,基本功能算是完成了,一些小的细节地方也考虑到了,但是还有其他的细节需要处理. 总体设计思路是,先画草图 -> 设计UI -> 编写UI代码 -> 编写CSS -> 编写JS逻辑代码: 面板(main-board) 面板

  • java 单播、广播、组播详解及实例代码

    java 单播.广播.组播详解及实例代码 在当前网络通信中(TCP/IP也不例外)有三种通信模式:单播.广播.组播(又叫多播, 个人感觉叫多播描述的有点不恰当),其中多播出现的时间最晚,但同时具备单播和广播的优点,最具有发展前景. 一.通信方式分类: 1.单播:单台主机与单台主机之间的通信: 2.广播:单台主机与网络中所有主机的通信: 3.组播:单台主机与选定的一组主机的通信: 二.单播:    单播是网络通信中最常见的,网络节点之间的通信 就好像是人们之间的对话一样.如果一个人对另外一个人说话

  • Python 字典的使用详解及实例代码

    目录 字典长什么样 字典内能放什么 访问字典内容 修改字典内容 删除字典数据 字典内置函数 字典是Python实现散列表数据结构的形式,表现映射的关系,一对一. 字典长什么样 {}这是一个空字典,可以看出字典是由两个花括号组成的. 在看这个{'a':1},这里面装了一对数据,'a'可称为键,1称为值 这个{'键1':'值1', '键2':'值2'}每一对数据 字典内能放什么 字典内的健是唯一的,在字典内所有内容中只存在一个,但值可以重复出现. 健只能是不变的值,比如字符串,数字,元组 值可以随意

  • Spring AOP  基于注解详解及实例代码

    Spring AOP  基于注解详解及实例代码 1.启用spring对@AspectJ注解的支持: <beans xmlns:aop="http://www.springframework.org/schema/aop"...> <!--启动支持--> <aop:aspectj-autoproxy /> </beans> 也可以配置AnnotationAwareAspectJAutoProxyCreator Bean来启动Spring对@

  • MyBatis获取数据库自生成的主键Id详解及实例代码

    MyBatis获取数据库自生成的主键Id详解及实例代码 在使用MySQL数据库时我们一般使用数据库的自增主键自动产生主键.如果在插入主表时,我们需要同时插入从表的数据,这时我们通常需要知道主表插入时自动产生的主键Id值. 下面介绍使用MyBatis进行插入时,如何同时获取数据库自生成的主键: 1.XML配置文件 <insert id="insert" parameterType="Person" useGeneratedKeys="true"

随机推荐