图解i++和++i

public class Main{
    public void a(){
        int i=9;  //之所以用9,不用0或者1是为了区别索引值。
        int j=i;
    }
    public void b(){
        int i=9;
        int j=i++;
    }
    public void c(){
        int i=9;
        int j=++i;
    }
}



这段代码编译后 javap -c Main.class 然后会生成

Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void a();
    Code:
       0: bipush        9    //将常量9压入栈顶。
       2: istore_1           //将栈顶元素弹出到本地局部变量第一个。也就是把9赋值给i。
       3: iload_1//把本地局部变量压入栈顶。
       4: istore_2//把栈顶元素弹出到本地局部变量第二个也就是j。
       5: return

  public void b();
    Code:
       0: bipush        9 //将常量9压入栈顶。
       2: istore_1//将栈顶元素弹出到本地局部变量第一个。也就是把9赋值给i。
       3: iload_1//把本地局部变量压入栈顶。
       4: iinc          1, 1//对本地第1个局部变量进行加1操作。也就是把i变成了10.
       7: istore_2//把栈顶元素弹出到本地局部变量第二个也就是j。
       8: return

  public void c();
    Code:
       0: bipush        9 //将常量9压入栈顶。
       2: istore_1/将栈顶元素弹出到本地局部变量第一个。也就是把9赋值给i。
       3: iinc          1, 1//对本地第1个局部变量进行加1操作。也就是把i变成了10.
       6: iload_1//把本地局部变量压入栈顶。也就是10压入栈顶。
       7: istore_2//把栈顶元素弹出到本地局部变量第二个也就是j。
       8: return
}

这里写图片描述

<think>我们正在讨论i++在编译前后的值变化。根据用户提供的引用,特别是引用[3]引用[5],我们可以深入分析。在Java中,i++是一个后缀自增操作符,它的行为可以分解为几个步骤。根据引用[3]:1.将变量i的当前值(自增前的值)压入操作数栈(用于表达式求值)。2.将局部变量表中的i自增1(即i=i+1)。3.操作数栈中的值(即自增前的值)被用于赋值或其他运算。因此,当我们有类似`i =i++;`的语句时,会发生以下步骤(引用[5]中的例子):-初始值:i=5-执行`i =i++`:a.将i的当前值5压入操作数栈(作为表达式的值)。b.将局部变量表中的i自增1(此时i变成6)。c.然后将操作数栈中的值(5)赋值给i(覆盖了6,所以i又变回5)。所以,最终i的值仍然是5。在编译层面,我们可以将`i =i++;`分解为以下伪代码:temp= i;//保存i的当前值(5)i= i+1;//i自增1(i变成6)i= temp;//将temp(5)赋值给i(覆盖成5)因此,编译后,i的值在赋值语句后变回了原来的值。另外,引用[4]中也有类似例子:`j=i++;`,这里j得到的是i自增前的值,而i自增1。但如果是`i =i++`,则自增操作被赋值操作覆盖了。总结:i++在表达式中的值是自增前的值,而自增操作发生在表达式求值之后(但在下一个序列点之前)。在赋值给同一个变量时,会导致自增被覆盖。下面我们用一个简单的Java代码示例来展示:```javapublicclass Test{public staticvoid main(String[]args){int i=5;i= i++; //这里i的值在自增后又被覆盖System.out.println(i); //输出5}} ```在编译后,上述代码的行为如上所述。注意:在C/C++中,类似`i= i++`的行为是未定义的(因为违反序列点规则),但在Java中,它是定义良好的,尽管结果可能不符合直觉。相关问题:1.为什么在Java中`i= i++`不会改变i的值?2.在C语言中,`i= i++`的行为是什么?3.如何正确使用自增运算符避免混淆?</think>### i++在编译前后的值变化分析 i++(后缀自增)在编译前后的值变化涉及编译器如何处理自增操作,其核心在于**值计算顺序****副作用发生时机**的区别。以下是详细分析: #### 1. **编译前代码(源代码)** ```java int i = 5; int j = i++; // j获取i的原始值,然后i自增 ``` #### 2. **编译后行为(字节码/JVM指令级)** 根据引用[3][^3]引用[4][^4],i++的操作分为三步: 1. **取值**:将变量`i`的当前值(5)压入操作数栈 ```java iload_1 // 加载变量i的值到操作数栈(值=5) ``` 2. **自增**:在局部变量表中对`i`执行自增操作 ```java iinc 1, 1 // 局部变量表的i自增1(i变为6) ``` 3. **赋值**:将操作数栈中的值(5)赋给目标变量 ```java istore_2 // 将栈顶值(5)存入j ``` #### 3. **值变化过程(时序分析)** | 步骤 | 操作数栈 | 局部变量表(i) | 说明 | |------|----------|---------------|------| | 初始 | [] | 5 | 初始状态 | | 取值 | [5] | 5 | 复制i的原始值到栈 | | 自增 | [5] | **6** | i在局部变量表中自增 | | 赋值 | [] | 6 | 栈顶值(5)赋给j | #### 4. **关键特性** 1. **表达式返回值** `i++`的返回值是**自增前的原始值**(引用[2][^2]): ```java int a = i++; // a=5 (i变为6) ``` 2. **与`++i`的区别** `++i`(前缀自增)先自增再返回值: ```java int b = ++i; // b=6 (i先变为6) ``` 编译后步骤相反:先自增再取值(引用[3][^3]) 3. **特殊场景:`i = i++`** 当赋值给自身时,自增被覆盖(引用[5][^5]): ```java int i = 5; i = i++; // 等效于: // 1. temp = i (5) // 2. i = i + 1 (6) // 3. i = temp (覆盖为5) ``` #### 5. **内存模型图解** ``` 源代码: j = i++ │ ▼ 编译后: [操作数栈] [局部变量表] │ │ ├─取值─────→ i=5 → 栈=5 │ │ ├─自增──────→ i=6 │ │ └─赋值───→ j=5 ``` #### 总结 - **编译前**:`i++`在代码中表现为"先使用后自增" - **编译后**:JVM将其拆解为**取值→自增→赋值**三步操作 - **值变化**:表达式返回原始值,变量自身在操作中自增 - **陷阱**:`i = i++`会导致自增被覆盖(引用[5][^5]) > 提示:在C/C++中此行为类似,但`i = i++`属于未定义行为(UB),而Java明确定义了执行顺序[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值