int i=0;
i=i++;
i=?很简单吧
先给出答案
然后说出为什么?虽然自己接触java时间也不短了,但是刚才看到那个帖子里的这个问题觉得很费解
看到答案我就觉得很奇怪,测试了一下果然是那样
看来自己真的还很菜,希望哪位能把我说懂原来的jFresH_MaN的帐号没分了,所以用马甲,各位见谅,欢迎讨论
100分!
i=i++;
i=?很简单吧
先给出答案
然后说出为什么?虽然自己接触java时间也不短了,但是刚才看到那个帖子里的这个问题觉得很费解
看到答案我就觉得很奇怪,测试了一下果然是那样
看来自己真的还很菜,希望哪位能把我说懂原来的jFresH_MaN的帐号没分了,所以用马甲,各位见谅,欢迎讨论
100分!
{
int i = 0;
public test()
{
i = i ++;
System.out.println(i);
}
public static void main(String argsp[]) {
new test();
}
}答案为0,如果i++改为++i,那答案应该就是1了,i = i ++ 是先赋值在加一的.
我问你
i++是在System.out.println(i);之前吧
为什么i还是0??照你的说法和我以前的理解,应该是这样
i=i++;=====>>>>
i=i;
i++对吧?
++运算符的优先级高于=,因此首先计算i++;(i++)的结果为0,同时作为表达式的一部分i的值成为1;然后执行=运算符,将(i++)的结果赋值给i;
因此最后i的值为0
i=0;
i++;//你是说这里等于1??++优先级应该是低于=的哦
i = i++;
i = i++;
i = i++;
i = i++;
i = i++;
i = i++;
System.out.println("i=" + i);
System.out.println("i=" + (i = i++));
System.out.println("i=" + (i = i++));
System.out.println("i=" + (i = i++));
System.out.println("i=" + (i = i++));
for (int j = 0; j < 5; ) {
j++;
System.out.println("j=" + j);
}
for (int j = 0; j < 5; ) {
System.out.println("j=" + j);
j++;
}等着楼主汇报学习结果,哈哈:)
i=i;
i++对吧?
________
对啊
如果i=++i;=====>>>>
i=i+1;
i=i;
c++ 里为1 (先赋值,再++)只有看汇编才能知道具体过程,c++的我看过,java没看
所以这种问题有意义吗?
i++;
System.out.println(i);请问这个结果是多少??为什么你们非要说i++之后等于0呢?
i=i++;
--------
int i=0;
i=i;
i++;
你们说这是不是等价的?
换句话说,
while(true){
i=i++;
}
永远执行i的值恒等于i的初始值,即使不是0也一样!下面我把过程写一下i=0;//假设此时内存地址为0x12345678
i=i++;//系统新开内存地址0x99999999,存放i原始值0,然后0x12345678的存放数据+1操作
//此时0x12345678=1,0x99999999=0,但是上一步是先给值,所以i的内存地址是0x99999999=0;所以i=0,但是,如果是
i=0;
i++;
此时i=1,因为0x99999999处新开辟的内存地址没有给任何引用,所以被丢弃了!i继续使用0x12345678处值
public void main(String[] args){
int i=0;
i = i++;
}
}看段汇编
public class T2 extends java.lang.Object{
public T2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: returnpublic void main(java.lang.String[]);
Code:
0: iconst_0 //将int类型常量0压入栈 (i的值在这就赋过了,所以是0)
1: istore_2 //将int类型值存入局部变量2
2: iload_2 //从局部变量2中装载值
3: iinc 2, 1 //局部变量2的值+1 (注意,只是局部变量!!!)
6: istore_2 //存入局部变量2中
7: return}
你的说法确实能解决这个问题
但是不知道,是否有根据当然不是我不相信你的说法
我只是想真正搞明白而已
{
int i = 0;
public test()
{
//i = i ++;//这个输出0,
//i = i ++;j=i;//j也输出为0,感觉这样的话i=i++;<->i=i;
//i++;//这个输出1
System.out.println(i);
}
public static void main(String argsp[]) {
new test();
}
}
//int i=0;
xor eax,eax
mov [addr],eax//i=i++;
mov edx,[addr]
mov [addr],edx
inc [addr]感觉c++比较符合人的思维
你的说法确实能解决这个问题
但是不知道,是否有根据当然不是我不相信你的说法
我只是想真正搞明白而已
===========================================
我没有依据,我凭我的直觉,因为从常理来讲,i=i++如果没有发生内存引用地址的变化就绝对不会等于零,尽管扶植操作先于+1动作发生,但是如果使用的是一块内存!则无论如何,i的值肯定是变化的,带着这个疑问,我构造了一个有穷循环
i=0;
for(int j=0;j<1000;j++){
i=i++;
}
结果i的埴是0!!!!把i的初始值该为其他任何数,都没有变化,这就说明虚拟机一定做了什么手脚,更改了内存的位置,使i始终指向初始植!我不明白为什么虚拟机这么做,难道是为了照顾多线程!?!
我的汇编水平比较差,你的C++代码我能看明白,他们一直是在一个地址上操作的
所以结果是1,这个没问题 java的汇编我还是没有明白,能不能再解释一下to blackhost(剑心)
按照你的说话
java里面的基本数据类型也有地址引用传值的咯?
这个还很新鲜啊
希望能有准确的根据
// i = i++;
0: iconst_0 //将int类型常量0压入栈 (i的值在这就赋过了,所以是0)
1: istore_2 //将int类型值存入局部变量2
2: iload_2 //从局部变量2中装载值
3: iinc 2, 1 //局部变量2的值+1 (注意,只是局部变量!!!)
6: istore_2 //存入局部变量2中
7: return //函数结束所以它是先赋值,i=i(即i=0),然后对一个局部变量进行了+1操作。
为了比较,看一下这个
//int i=0;
//i=++i;
Code:
0: iconst_0 //同上
1: istore_2 //同上
2: iinc 2, 1 //!!!
5: iload_2 //!!!
6: istore_2
7: return
------------------------------------------
不知道JVM为什么这么做,但它的确是这么做的。
按照你的说话
java里面的基本数据类型也有地址引用传值的咯?
这个还很新鲜啊
希望能有准确的根据
==========================
我也觉得新鲜,今天我也是头一次遇到,我只是根据常识判断然后加编码验证,至于后面的机理部分我也不太清楚,虚拟机为何这样做恐怕的问JVM的开发者了!
java有太多的地方这样了,对String b="a"而言后面就有很多操作!那虚拟机为什么这么做呢,开始头痛了,算了,我还是不死大脑细胞了.....
一个是先对局部变量+1,然后赋值
仔细研究了一下两段代码的差别在这里
2: iload_2
3: iinc 2, 1
------------
2: iinc 2, 1
5: iload_2
其他都是一样的而这恰好说明了第一个是先赋值iload_2 后++iinc 2, 1
第二个是先iinc 2, 1 后赋值
只是顺序不同
而没有说明其他问题啊楼上说的局部变量的问题,我觉得变量都是一样的啊可能是我的理解能力不行
希望赐教
javap -v 也可以生成一片....
3: iinc 2, 1 //v+1
------------
2: iinc 2, 1 //v+1
5: iload_2 //i=v
好象是这样子
class Test
{
public:
int i;public:
Test()
: i(0)
{
i = i++;
}
};对应的汇编代码:
i(0):
mov edx [addr];
xor ecx ecx;
mov [edx] ecx;
i = i++;
mov eax [addr];
xor edx [eax]; 这里edx = 0
inc dword ptr [cax]; 这个地方为i++
mov ecx [addr];
mov [ecx] edx 这里执行i = i,但是后面的i = edx = 0;
3: iinc 2, 1 //局部变量2中的值+1, 这个加了也没用,并没赋给i
------------
2: iinc 2, 1 //局部变量2中的值+1
5: iload_2 //将局部变量中的值取出来,赋给i(不一样了吧)
我大概明白你的意思呢
i赋值之后就出栈了,之后也没有被赋值
kingfish(八百里秦川@龙城异客)
和
blackhost(剑心)
说的是一回事=操作的时候,jvm会使用一个临时变量计算等号右边的值。
而因为后++优先级低于=,所以就先赋了值,而后的++操作还是在临时变量进行加操作,所以没有改变原来的变量值。。
嗯,这个解释和javap的结果是一样的blackhost(剑心)给出的是猜测,而kingfish(八百里秦川@龙城异客)给出了根据
谢谢两位,我准备另开贴每人给100分
这个贴先不结,留着大家谈论发表自己的意见:)
前缀方式先进行递增运算,而后缀方式先引用变量原先的值~~`再看下面例子
int m=5;
int n=5;
int a=2* ++m;//现在a的值是12,m是6
int b=2* n++;//现在b的值是10,n是6
我最讨厌用++了,因为这会导致代码令人迷惑,产生烦人的bug~
java象这种问题还是很多的..
希望能和大家共同研究:)
i=i++;
i=?
这真是奇怪的问题,按理论上讲:i++与++i或--i i--是结果是一样的,但是java有个奇怪的规定:如果讲增量运算和减量运算表达试再做为其他表达试的操作数使用时,++i与i++是有区别的:i++是在使用之后,使i的值加1,so执行完i++后,整个表达试的值为i,而i的值变为i+1,++i在使用i之前,,使i加1,so执行完++i后,这个表达试的值和i的值均为i+1.
所以我觉得这个应该是0,呵呵 笨鸟学习中,谢谢大家继续讨论。
关键原因是Java编译器对一个表达式的处理总是: "把operand入栈进行运算,最后弹栈赋值给表达式结果",就算是其实这个过程是多余的,Java编译器也傻乎乎的这么做。如果是用Sun JDK 1.4.2,看看下面的代码
int i=0;
i = i++;
i = ++i;//i = 0
0: iconst_0 //常量0进操作数栈
1: istore_2 //栈顶的值放入变量2,并弹栈 (i=0)
//i = i++
2: iload_2 //把变量2(i)的值0进栈
3: iinc 2, 1 //i++, 注意:此步操作是对变量2(i)操作,
//不是对栈顶元素操作,问题就在这!!!
6: istore_2 //把栈顶值0弹出来赋给变量2,结果i还是等于0,这导致了iinc白做了
整个过程是把操作数放进栈,其实没用操作数栈,直接对局部变量操作了++,关键在于Java的编译器认为表达试最终应该从操作数栈,弹栈取值,结果把旧值放进变量i了,(这里其实栈操作是多余的,如果不做栈操作,那么结果就应该是1了)下面就不一样了
//i = ++i;
7: iinc 2, 1 //++i
10: iload_2 //把变量2(i)的值1进栈
11: istore_2 //把栈顶值1弹出来赋给变量2,结果i是等于1
其实10,11是多余的,编译器如果足够聪明应该可以跳过,但是编译器还是认为表达试最终应该从操作数栈,弹栈取值。这种编译出来的代码是比较规范的,其实应该有优化掉10,11,Java的编译器才够聪明我用了IBM的JDK和号称对服务端计算做优化的JRocket,除了变量名内部表示不一样(istore2变成istore1),其实结果也一样.个人认为这是各厂商的编译器优化不足,但是或许byte code级不用做太多的优化,因为VM把byte code解释成本地代码的时候应该会做优化,除非你用了volatile阻止指令执行序列优化来避免多线程下JMM暴露的问题。
不论是前++还是后++,只是他们一个是右关联,一个是左关联。看了大家说得,好像还是没有真正的找到原因啊。关注……
i=i++;//系统新开内存地址0x99999999,编译器先新开辟一个空间0x99999999存i原值0.再对i自增,也就是在0x12345678位置是i+1。当完成自增以后要赋值,因为是后++,所以要把0x99999999空间的值赋给=另一侧的变量。因为那个变量也是i,就是在空间0x12345678的,所以在空间0x12345678的i经过赋值后又变成0了。
最后把新开辟的临时空间0x99999999释放。
i = i++;结果i=0;看看这段代码的结果。int i=0;
i = (i++);结果竟然 ...
谢谢两位,我准备另开贴每人给100分
这个贴先不结,留着大家谈论发表自己的意见:)
=================================================
昨天玩游戏去了!没有继续讨论,BTW,还有新给的分啊~,呵呵,先谢了啊!我就喜欢爽快的同志!我觉得下面这篇英文说的比较透彻了!原来人家2000年就讨论过啊!我今早刚搜到
Sorry, but I was very confused. It has been a while since I looked at our
bytecode rewriter, and it seemed that I presented my problem completely wrong.<skip>
The actual problem is the following:public class Test {
String str="";
public void incrementStr() {
str +="a";
}
}The str+="a" statement in translated into:
This is translated into:
0 new #5 <Class java.lang.StringBuffer>
3 dup
4 aload_0
5 dup_x2
6 getfield #10 <Field java.lang.String str>
9 invokestatic #12 <Method java.lang.String valueOf(java.lang.Object)>
12 invokespecial #8 <Method java.lang.StringBuffer(java.lang.String)>
15 ldc #2 <String "a">
17 invokevirtual #9 <Method java.lang.StringBuffer append(java.lang.String)>
20 invokevirtual #11 <Method java.lang.String toString()>
23 putfield #10 <Field java.lang.String str>So dup_x2 at line 5 is the shitty instruction that I don't want.Note compiling str = str + a; leads to code without stack insertions 0 aload_0
1 new #5 <Class java.lang.StringBuffer>
4 dup
5 aload_0
6 getfield #10 <Field java.lang.String str>
9 invokestatic #12 <Method java.lang.String valueOf(java.lang.Object)>
12 invokespecial #8 <Method java.lang.StringBuffer(java.lang.String)>
15 ldc #2 <String "a">
17 invokevirtual #9 <Method java.lang.StringBuffer append(java.lang.String)>
20 invokevirtual #11 <Method java.lang.String toString()>
23 putfield #10 <Field java.lang.String str>
</skip>I am pretty convinced that javac doesn't offer any options to programmers to
keep the compiler from putting dupx_2 instructions into the generated bytecode?Does anybody has an exhaustive list of all the cases where javac generates
dup_x1 and dupx_2 instructions?Sorry for the confusion againEddy Truyen.Steven Carroll wrote:
<表达式>::=([+|-]<项>)的正闭包
<赋值语句>::=<表识符>=<表达式>那么赋值语句i=i++;的语法树推导可能是
赋值语句
=>
i = <表达式>
=>
i=<项>
=>
这时,<项>可以推到成i++,这表示"表达式i"计算完后,对i自增,注意,是表达式,不是赋值语句!!!因此可以知道,
在Java里,i=i++;的语法树是
i = i++
/ | \
i = <表达式>
所以编译器完成语法分析后,代码生成相对应产生的byte code是,入栈(对应语法树右边的i),变量自增(对应<标示符>++),出栈赋值(对应赋值语句,把表达式在栈上运算的结果放入内存)的三条byte code执行码JAVA的有的是编译期的,比如
for () {
int i=0;
i = ...;
}
编译出来的结果和
int i=0;
for () {
i = ...;
}
一样(避免N次 常量0入栈,弹栈对i赋值 的两个执行码操作)
有的是真正执行的时候,VM做的优化,这里,我猜测VM应该有优化吧?
说的意思是=表达式右边式在栈里面计算的
这个结论和昨天讨论的结论式一样的也就是说在++之前已经返回了计算的值给了等号左边,所以后面的++是在栈里的临时变量进行计算的
,这样就不会影响原来的变量值。我想这个应该是最终结论了吧?大家还有什么看法?
这个就是里那个问题
我看见有很多在你那个帖子里说的不对
想让大家一起针对这个问题讨论一下
所以开贴又问,这样就能让大家都对jvm的运行机制有个深入的了解
答案是1嘛?
请运行一下再说
:)
借着这个贴,再问问大家
由谁仔细研究过<<Inside JVM>>这本书那上面是不是对于这方面是不是介绍比较多?另外希望以后Java版都点技术上的讨论,这样人气才会旺。
谢谢大家,我又学到了不少!!
我是说因为编译器比较规板,把赋值语句i=i++
生成了i入栈,计算表达式,出栈赋值i的三板斧,
但是这里计算表达式不是在栈上计算的!
所以导致一次多余的入栈和弹栈,清除了本来已经计算好的结果你看看iinc的JVM Spec描述
iinc OperationIncrement local variable by constant
Format
iinc
index
const
Operand Stack No change看到了没有,No Change!!!iinc是直接对变量操作的,不是把操作数放到stack计算的,有点特殊是不是?和传统语言不太一样。你在栈上没有计算,正确的过程是你把i的值0放到栈上
然后把i变成1,但是栈上还是0
最后把栈上的0弹出来赋值给i,结果把刚才iinc的结果给冲掉了,i又变成了0
-----------------------------------------
注意,i现在并不是在局部变量中,而是在栈中(当前的操作数)2: iload_2 //i=局部变量2中的值
3: iinc 2, 1 //局部变量2中的值+1, 这个加了也没用,并没赋给i
------------
2: iinc 2, 1 //局部变量2中的值+1
5: iload_2 //将局部变量中的值取出来,赋给i(不一样了吧)-----------------------------------------------------
>>这个加了也没用,并没赋给i
不是的,这个是不操作栈的,直接对i操作的
这个很特殊,你可以看看iinc的VM Spec, definition那部分
<<Inside JVM>>如何?
其实说白了就是,=操作先进行了
后来的++执行就是在栈外的变量上执行了
最终没有对栈内的i赋值,对吧?
-------------------------------
不是的,是=操作晚,++早
但是++是对变量的,=是弹栈的
因为++没有对栈操作而是直接改了i
最后=操作又弹栈取出来刚刚放进去没有运算过的0
结果导致iinc白白作了一遍,又被弹栈出来的0重新赋值了
是网站,楼主大哥,我为了发帖子,输入法都把高频词挪到前面了,呵呵
我看懂了,又多学了一点,呵呵~
其实买了inside JVM很久了,没怎么自己看。看来jvm还很值得研究啊!
#include <stdio.h>
int main(void)
{
int i = 0;
i = i++;
printf("i = %d",i);
}
得到的结果也是i = 0;
查汇编的代码有一段是这样的:
movl $0 , -4(%ebp) //int i = 0;
movl -4(%ebp) , %edx // %edx <- i
leal -4(%ebp) , %eax // %eax <- i
incl (%eax) // %eax 中的数++
movl %edx , -4(%ebp) // %以下几行取%edx中的数打印,返回
movl -4(%ebp) , %eax // %eax中的值++后没有被引用过
movl %eax , 4(%esp)
movl $.LC0 , (%esp)
call printf
leave
ret汇编我不太懂,注释里是我对这段代码的理解,应该和java上的情况类似吧
public static void main(String[] args){
int i = 0;
i = i++;
System.out.println(i);
int j = 0;
j = (j++);
System.out.println(j);
}
}
//JDK 1.4.2_b05
//javac Test.java
//java Test//out println
0
0
表达式的值得以执行。有一种表达式叫简单赋值表达式。
其结构如下:left-hand = righ-hand简单赋值表达式的评估步骤如下:
1 对left-ha估以nd评确定一个变量。
2 对right-hand评估以得到一个值。
3 将步骤2得到的值保存进步骤1所确定的变量中。简单赋值表达式的值即是 赋值以后,left-hand所确定的变量的值。有一种表达式叫后增表达式。
其结构为:
operand ++后增表达式的评估步骤如下:
1 对operand评估以确定一个变量。
2 对步骤1中的变量进行增1操作。后增表达式的值是 没有增1操作前operand所确定的变量的值。回过来分析(i的初始值为0)
i=i++i=i++ 是简单赋值表达式,所以其评估过程是1 确定变量i
2 评估i++
2.1 确定变量i
2.2 i增1,这时i的值为1
3 i++表达式的值是0,于是i的值被赋为0如上分析,最后i的值为0。