现在的位置: 首页 > 综合 > 正文

java 中关于i = i++; 的bug

2017年12月11日 ⁄ 综合 ⁄ 共 1573字 ⁄ 字号 评论关闭

昨日写了个appWidget 的demo 优化代码的时候偶然写出了一句 num = (num++)%3;的代码,结果出现了莫名奇妙的问题。我尝试对上一句代码循环几次,发现num 的值一直都是0;

百思不得其解下写了个demo 验证,demo如下。 环境java 6.0;

public class testpro {
    public static void main(String[] args) {
        int num = 0;
        while(num < 2) {
            //num = (num++)%3;
            num = num++;
        }
    }
}

PS: 如果如上写的代码就会出现死循环,因为num 的值一直为0;  而同样的代码在gcc 编译通过 运行结果正常。

查看java 的 bytecode 如下:

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

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_3
   4:   if_icmpge       15
   7:   iload_1
   8:   iinc    1, 1
   11:  istore_1
   12:  goto    2
   15:  return

}

上面的代码分析

1. i_const_0 //把int 类型的常量0 压入操作数的栈顶。

2. istore_1 //把操作数栈的栈顶出栈,并存入局部变量中。

3. iload_1 //把局部变量中的第一个值 复制到操作数栈中。

4. iconst_3 //把常量3压入操作数栈顶,

5. if_icmpge // 此操作是将操作数栈顶的两个值出栈,比较值1和值2的大小,如果值2大于等于值1 则跳转到 15 执行。

6. iload_1 //把变量1的值压入操作栈的栈顶。

7. iinc      1,1 //变量1 的值加1 从0 变成了1. 但是此时操作栈栈顶的值依然是 刚才压入栈的0!

8. istore_1 //这个操作又把操作栈的栈顶出栈赋值给了变量。 这个时候 变量的值有被从1 赋值成0了。  画外音:这不科学啊,这毁三观啊!

9. 跳到2 循环。

这真的不科学啊~~~~~~~~~~ java 你肿么可以这么不科学呢?!

好吧,只能怪自己怎么写出了这么不科学的代码- -!。

下附 类似代码在gcc下编译出的结果。

int main() {
    int num = 0;
    while(num < 2) {
        num = num++;
    }
    
    return 0;
} 

以上代码运行正常, num可以增加,while运行两次后推出循环。

下附 gcc -s 之后编译出来的汇编文件

        .file   "test.c"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    $0, -4(%rbp)           //对 %rbp-4 的地址进行赋值 0
        jmp     .L2
.L3:
        addl    $1, -4(%rbp)           //对 %rbp-4 的地址进行加1赋值
.L2:
        cmpl    $1, -4(%rbp)           //而进行比较的时候, 取的值也是%rbp-4 的地址的值。
        jle     .L3
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.4.6-11ubuntu2) 4.4.6"
        .section        .note.GNU-stack,"",@progbits

相比java而言,逻辑清晰明了。




抱歉!评论已关闭.