“ x = x ++”之后的x是什么?


285

执行此操作后(幕后)会发生什么?

int x = 7;
x = x++;

也就是说,何时在一个语句中对变量进行后递增并分配给自己?我编译并执行了这个。即使整个陈述之后x仍然是7 。在我的书中,它说是递增的!x


9
试试这个:int x = 7; x = ++x;,当然仍然是可怕的代码,您不需要重新分配。int x = 7; x++;足够。
stivlo 2011年

7
这是一种非常糟糕的做法,请不要在使用它的同一行中递增变量。
Yousf 2011年

5
我更喜欢使用x += 1,除非可能是循环的。for(int x=0; x<7; x++)
Svish

2
@andyortlieb没有对象,只是一个基本值。
fortran

Answers:


301

x确实增加了。但是,您正在将xback 的旧值分配给它自己。


x = x++;
  1. x++递增x并返回其旧值。
  2. x = 将旧值分配回自己。

因此,最后x将其分配回其初始值。


3
然后,您将对x = ++ x怎么说?
Hisham Muneer 2014年

3
x在这种情况下,@ HishamMuneer 在读取之前先递增x + 1

@HishamMuneer为时已晚。但我将其放在此处,因为它可能对将来会遇到的其他人有所帮助。理解此问题的最佳方法是查看为x = x ++和x = ++ x创建的汇编代码。另请参阅Thinkcap的答案。
nantitv

我知道这已经很老了,但是我有一个问题。标准是否保证上述操作顺序?赋值是否有可能在增量之前执行?
翡翠武器

@EmeraldWeapon它是用Java定义的。您只有在C / C ++中才能看到这种恶作剧。
Mysticial

385
x = x++;

相当于

int tmp = x;
x++;
x = tmp;

46
大声笑,是的,递归定义。您可能应该这样做,x=x+1而不是x++
user606723,2011年

8
@ user606723:不,我指的是整个声明x = x++,而不仅仅是发布增量x++
约翰·卫斯理亲王

20
如果没有进一步的解释,我认为这没什么用。例如,也并不x = ++x;等同于int tmp = x; ++x; x = tmp;,因此我们可以通过什么逻辑推断出您的答案正确(正确)?
2011年

4
更清楚的是,这是asm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker

3
@forker:我认为如果您使用了适用于迈克尔正在使用的处理器的汇编指令,将会更清楚;)
卡尔

258

该声明:

x = x++;

等效于:

tmp = x;   // ... this is capturing the value of "x++"
x = x + 1; // ... this is the effect of the increment operation in "x++" which
           //     happens after the value is captured.
x = tmp;   // ... this is the effect of assignment operation which is
           //     (unfortunately) clobbering the incremented value.

简而言之,该声明无效。

重点:

  • 后缀递增/递减表达式的值是递增/递减发生之前的操作数的值。(对于“前缀”格式,该值为操作的操作数的值,)

  • 将值分配给LHS 之前,已对赋值表达式的RHS进行了完全评估(包括任何增量,减量和/或其他副作用)。

请注意,与C和C ++不同,Java中表达式的求值顺序是完全指定的,并且没有平台特定变体的空间。从当前线程的角度来看,只有在编译器不能改变执行代码的结果的情况下,编译器才可以对其进行重新排序。在这种情况下,将允许编译器优化整个语句,因为可以证明它是无操作的。


如果还不是很明显:

  • “ x = x ++;” 几乎可以肯定,任何程序都是错误的。
  • OP(用于原始问题!)可能表示“ x ++;”。而不是“ x = x ++;”。
  • 将auto inc / decrement和赋值结合到同一变量上的语句很难理解,因此无论其正确性如何,都应避免使用。根本不需要编写这样的代码。

希望像FindBugs和PMD这样的代码检查器可以将这样的代码标记为可疑。


7
顺便提一句,OP,您可能想说x++而不是x = x++
乔恩·纽缪斯

3
正确,但可能要强调,增量发生右手表情评估后,但分配给左手,因此是明显的“覆盖”
波希米亚

2
看起来像是那些高中编程的扭曲者之一。
kumarharsh 2012年

1
@Alberto-很高兴听到您没有将“专家”陈述作为“福音真理”。但是,一种验证我所说内容的更好方法是咨询JLS。您的编译/反编译测试仅显示我所说的内容对一个Java编译器有效。其他人可能(假设地)有不同的行为……只是JLS不允许这样做。
斯蒂芬·C

4
仅供参考:这最初是发布给另一个问题,该问题已作为该问题的副本被关闭,现已合并。
Shog9

33
int x = 7;
x = x++;

它在C中具有未定义的行为,对于Java,请参见此答案。这取决于编译器会发生什么。


4
不,它不取决于您引用的答案的编译器-请编辑
暂时

@Mr_and_Mrs_D然后取决于什么?
Mac:

2
对于C_,这是未定义的behavior_。即使这样说,取决于编译器也会产生误导作用-这意味着编译器应该指定这种行为。我恢复投票,但考虑编辑您的答案-编辑:哎呀,我不能-您必须首先编辑它:D
Mr_and_Mrs_D 2014年

16

类似的构造x = x++;表明您可能误解了++操作员的工作:

// original code
int x = 7;
x = x++;

让我们基于删除++运算符来重写此代码以执行相同的操作:

// behaves the same as the original code
int x = 7;
int tmp = x; // value of tmp here is 7
x = x + 1; // x temporarily equals 8 (this is the evaluation of ++)
x = tmp; // oops! we overwrote y with 7

现在,让我们重写它以执行您想要的操作(我认为):

// original code
int x = 7;
x++;

此处的微妙之处在于,++运算符修改了变量x,而不是像那样的表达式,表达式的x + x求值结果为int值,但变量x本身保持不变。考虑像古老for循环这样的构造:

for(int i = 0; i < 10; i++)
{
    System.out.println(i);
}

注意i++那里的吗?是同一位运算符。我们可以这样重写该for循环,其行为相同:

for(int i = 0; i < 10; i = i + 1)
{
    System.out.println(i);
}

我还建议++在大多数情况下,不要在较大的表达式中使用运算符。由于修改前后增量(++xx++分别为)修改原始变量的微妙之处,很容易引入难以跟踪的细微错误。


13

根据从类文件获得的字节码

两种分配都增加x,但不同之处在于 when the value is pushed onto the stack

在中Case1,在增量之前(基本上意味着您的增量不执行任何操作),先进行推送(然后再分配)

在中Case2,首先发生增量(使之为8),然后将其推入堆栈(然后分配给x)

情况1:

int x=7;
x=x++;

字节码:

0  bipush 7     //Push 7 onto  stack
2  istore_1 [x] //Pop  7 and store in x
3  iload_1  [x] //Push 7 onto stack
4  iinc 1 1 [x] //Increment x by 1 (x=8)
7  istore_1 [x] //Pop 7 and store in x
8  return       //x now has 7

情况2:

int x=7; 
x=++x;

字节码

0  bipush 7     //Push 7 onto stack
2  istore_1 [x] //Pop 7 and store in x
3  iinc 1 1 [x] //Increment x by 1 (x=8)
6  iload_1  [x] //Push x onto stack
7  istore_1 [x] //Pop 8 and store in x
8  return       //x now has 8
  • 此处的堆栈是指操作数堆栈,本地:x索引:1类型:int

你能详细解释一下你的答案吗?
Nihar

请看一下引用的链接和评论
Evenprime


8

后增量运算符的工作方式如下:

  1. 存储操作数的先前值。
  2. 递增操作数的值。
  3. 返回操作数的前一个值。

所以声明

int x = 7;
x = x++; 

评估如下:

  1. x初始化为值7
  2. 后增量运算符存储x的先前值(即7)以返回。
  3. 增加x,所以现在x是8
  4. 返回x的前一个值,即7,并将其分配回x,因此x再次变为7

因此x确实增加了,但是由于x ++将结果分配回x,因此x的值被覆盖为其先前的值。


但在MSVC x是8. GCC和铛X是为7
夏日的阳光

7

增量发生在调用x之后,因此x仍然等于7。++ x在调用x时等于8


7

当您重新为其分配值时,x它仍然为7。尝试x = ++x,您将获得8的其他值

x++; // don't re-assign, just increment
System.out.println(x); // prints 8

6

因为x ++在将值赋给变量之后会增加其值。以此类推,并在执行此行期间:

x++;

varialbe x仍将具有原始值(7),但在另一行上再次使用x,例如

System.out.println(x + "");

会给你8。

如果要在赋值语句中使用x的增量值,请使用

++x;

这会将x递增1,然后将该值分配给变量x。

[编辑]只是x ++,而不是x = x ++;前者将x的原始值分配给它自己,因此它实际上在那一行上什么也不做。


而其中,说它分配后增加,以及一个表示将打印8.增加分配之前,和它打印7
R.费尔南德斯Martinho

如果x最初为7,System.out.println(String.valueOf(x ++)); 打印7.您确定我们在谈论相同的编程语言吗?
josephus

我是。这ideone.com/kj2UU不打印8,这样的答案索赔。
R. Martinho Fernandes

是的,我错了。x = x ++将在递增x之前先为x分配7。由于x ++(本身就是赋值)在x =(无论如何)之前首先解析,因此在x =(无论如何)中分配给x的值将紧随其后。对不起,我没有看到。
josephus

1
实际上,增量是第一件事ideone.com/xOIDU
R. Martinho Fernandes

4

什么时候发生 int x = 7; x = x++;

ans-> x++表示先将x的值用于表达式,然后将其增加1。
这就是您的情况。将RHS上的x的值复制到LHS上的变量x,然后将其值x增加1。

类似地,++x 意味着 ->先将x的值加1,然后在expression中使用。
因此,如果您这样做 x = ++x ; // where x = 7
,则将获得8的值。

为了更清楚,请尝试找出多少个printf语句将执行以下代码

while(i++ <5)   
  printf("%d" , ++i);   // This might clear your concept upto  great extend

不正确“将RHS上的x的值复制到LHS上的变量x,然后将x的值增加1”-这将x是8,但它是7-读取和分配之间发生增量
user85421,2008年

3

++x被预增量 ->x递增之前被使用
x++是后递增->x递增正在使用

int x = 7; -> x get 7 value <br>
x = x++; -> x get x value AND only then x is incremented

1

所以这意味着: x++不等于x = x+1

因为:

int x = 7; x = x++;
x is 7

int x = 7; x = x = x+1;
x is 8

现在似乎有点奇怪:

int x = 7; x = x+=1;
x is 8

非常依赖编译器!


2
谁说它首先是平等的?
fortran

1
如果我是您,我会立即将这些书丢掉xD无论如何,这就像(x = x + 1, x-1)在C中一样,其中允许使用逗号分隔的表达式。
fortran

3
@fortran:好吧,在我第159页的《 Java编程语言,第三版》已有十多年的历史了,它说:“表达式i ++等同于i = i + 1,但我只被评估一次。” ?它摆在首位詹姆斯·高斯林,它会出现在这个版本的Java规范的这部分是非常模糊的,指定不佳,我相信以后的版本清理更清楚地表达实际的操作语义的语言。
埃里克利珀

2
@fortran:通过“除了我仅被评估一次”,标准试图传达一个表达式,例如“ M()。x ++”仅调用一次M()。一个不太模糊的,更准确的措辞强调,之间是有区别评价我作为一个变量来确定其存储位置 -这是什么是“只计算一次”在这里的意思-和读取或写入到存储位置 - -两者都可能是对“评估”的合理但不正确的解释。显然,必须同时读取和写入存储位置!
埃里克·利珀特

1
“非常依赖编译器” -一点都不!
斯蒂芬·C

-1

x = x ++;

这是后增量运算符。应该理解为“使用操作数的值,然后递增操作数”。

如果要进行相反操作,即“递增操作数,然后使用操作数的值”,则必须使用如下所示的pre-increment运算符。

x = ++ x;

该运算符首先将x的值加1,然后将其赋回x。


-1

我认为无需编写代码也只需思考就可以解决这个争议。

考虑功能i ++和++ i,例如Func1&Func2。

现在我= 7;
Func1(i ++)返回7,Func2(++ i)返回8(每个人都知道这一点)。在内部,两个函数都将i增至8,但是它们返回不同的值。

因此,我= i ++调用了函数Func1。在函数内部,我递增到8,但完成后函数返回7。

因此最终将7分配给i。(所以最后,我= 7)


2
这里没有有效的“争议”。该代码可证明以特定方式运行,并且该行为符合JLS。任何认为它行为不同的人要么没有尝试过,要么被迷惑了。(这有点像说7 x 7是49,这是在有人忘记时间表的情况下“引起争议的……”)
Stephen C

-2

这是因为您使用了后递增运算符。在下面的代码行中

x = x++;

发生的是,您正在将x的值分配给x。在将x的值分配给x之后,x ++将x递增。这就是后增量运算符的工作方式。他们在执行语句后开始工作。因此,在您的代码中,x先返回,然后再递增。

如果你做了

x = ++x;

答案是8,因为您使用了预递增运算符。这将在返回x的值之前先递增该值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.