O que é x depois de "x = x ++"?

285

O que acontece (atrás das cortinas) quando isso é executado?

int x = 7;
x = x++;

Ou seja, quando uma variável é pós-incrementada e atribuída a si mesma em uma instrução? Eu compilei e executei isso. xainda é 7, mesmo depois de toda a declaração . No meu livro, diz que xé incrementado!

Michael
fonte
9
Tente o seguinte: int x = 7; x = ++x;é claro que ainda é um código horrível, você não precisa reatribuir. int x = 7; x++;basta.
Stivlo 27/10
7
Essa é uma prática muito ruim, não aumente a variável na mesma linha em que você a usa.
Yousf 27/10/11
5
Eu preferiria usar x += 1, exceto talvez em loops. for(int x=0; x<7; x++)
Svish
2
@andyortlieb não há objeto, apenas um valor básico.
fortran

Respostas:

301

xé incrementado. Mas você está atribuindo o valor antigo de xvolta a si mesmo.


x = x++;
  1. x++incrementa xe retorna seu valor antigo.
  2. x = atribui o valor antigo de volta a si mesmo.

Portanto, no final, xé atribuído de volta ao seu valor inicial.

Mysticial
fonte
3
Então, o que você dirá sobre x = ++ x;
Hisham Muneer
3
O @HishamMuneer xé incrementado primeiro antes de ser lido nesse caso, então você termina com x + 1.
@HishamMuneer É tarde demais. Mas estou colocando aqui porque pode ser útil para outras pessoas que procurarão no futuro. A melhor maneira de entender esse problema é observar o código de montagem criado para x = x ++ e x = ++ x. Por favor, veja a resposta do Thinkingcap também.
N
Eu sei que isso é super antigo, mas tenho uma pergunta. A ordem de operação acima é garantida pela norma? É possível que a atribuição seja executada antes do incremento?
Emerald Weapon
@EmeraldWeapon É definido em Java. É apenas em C / C ++ que você vê esse tipo de travessuras.
Mysticial
385
x = x++;

é equivalente a

int tmp = x;
x++;
x = tmp;
Prince John Wesley
fonte
46
Lol, yay para definições recursivas. você provavelmente deveria ter feito em x=x+1vez dex++
user606723 27/10
8
@ user606723: Não. Eu quis dizer toda a declaração x = x++, não apenas o pós-incremento x++.
Prince John Wesley
20
Não acho que isso seja tão útil sem maiores explicações. Por exemplo, não é verdade que isso x = ++x;também seja equivalente int tmp = x; ++x; x = tmp;, então por que lógica podemos deduzir que sua resposta está correta (qual é)?
Kvb 27/10/11
4
ainda mais claro é em asm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker
3
@ forker: Eu acho que seria mais claro se você usasse as instruções de montagem que se aplicam ao processador que Michael está usando;)
Carl
258

A declaração:

x = x++;

é equivalente a:

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.

Em suma, a declaração não tem efeito.

Os pontos principais:

  • O valor de uma expressão de incremento / decremento do Postfix é o valor do operando antes que o incremento / decremento ocorra. (No caso de um formulário Prefixo, o valor é o valor do operando após a operação)

  • o RHS de uma expressão de atribuição é completamente avaliado (incluindo quaisquer incrementos, decrementos e / ou outros efeitos colaterais) antes que o valor seja atribuído ao LHS.

Observe que, diferentemente de C e C ++, a ordem de avaliação de uma expressão em Java é totalmente especificada e não há espaço para variações específicas da plataforma. Os compiladores só podem reordenar as operações se isso não alterar o resultado da execução do código da perspectiva do encadeamento atual. Nesse caso, seria permitido a um compilador otimizar toda a instrução porque pode ser provado que não é uma operação.


Caso ainda não seja óbvio:

  • "x = x ++;" é quase certamente um erro em qualquer programa.
  • O OP (para a pergunta original!) Provavelmente significava "x ++;" em vez de "x = x ++;".
  • As declarações que combinam aumento / redução automática e atribuição na mesma variável são difíceis de entender e, portanto, devem ser evitadas independentemente de sua correção . Simplesmente não há necessidade de escrever código assim.

Felizmente, verificadores de código como FindBugs e PMD sinalizarão códigos como suspeitos.

Stephen C
fonte
7
Como uma nota lateral, OP, você provavelmente quer dizer apenas em x++vez de x = x++.
Jon Newmuis
3
Correta, mas talvez enfatizar que o incremento acontece pós avaliação da mão direita expressão, mas pré atribuição para o lado esquerdo, daí a aparente "de substituição"
Bohemian
2
parece um daqueles truques de programação do ensino médio ... bom para esclarecer o básico!
precisa saber é o seguinte
1
@ Alberto - É bom saber que você não toma declarações "especializadas" como "verdade do evangelho". No entanto, uma maneira melhor de validar o que eu disse seria consultar o JLS. Seu teste de compilação / descompilação mostra apenas que o que eu disse é válido para um compilador Java. Outros (hipoteticamente) poderiam se comportar de maneira diferente ... exceto que o JLS não permite isso.
Stephen C
4
Apenas um FYI: foi originalmente publicado em uma pergunta diferente, que foi encerrada como duplicata e agora foi mesclada.
precisa saber é o seguinte
33
int x = 7;
x = x++;

Ele tem um comportamento indefinido em C e, para Java, veja esta resposta . Depende do compilador o que acontece.

user712092
fonte
4
Não, não depende o compilador de acordo com a resposta que você citou - editar por favor - -1 por agora
Mr_and_Mrs_D
@Mr_and_Mrs_D Então depende do que?
Mac
2
É indefinido behavior_only para C_. Mesmo assim, dizer que depende do compilador é enganoso - implica que o compilador deve especificar esse comportamento. Eu reverto meu voto, mas considere editar sua resposta - edit: oops, não consigo - você deve editá-lo primeiro: D
Mr_and_Mrs_D
16

Uma construção como x = x++;indica que você provavelmente está entendendo mal o que o ++operador faz:

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

Vamos reescrever isso para fazer a mesma coisa, com base na remoção do ++operador:

// 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

Agora, vamos reescrevê-lo para fazer (o que eu acho) que você queria:

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

A sutileza aqui é que o ++operador modifica a variávelx , diferente de uma expressão como x + x, que seria avaliada para um valor int, mas deixaria a xprópria variável inalterada. Considere uma construção como o forloop venerável :

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

Observe o i++lá dentro? É o mesmo operador. Poderíamos reescrever esse forloop assim e ele se comportaria da mesma maneira:

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

Também recomendo não usar o ++operador em expressões maiores na maioria dos casos. Devido à sutileza de quando ela modifica a variável original antes e depois do incremento ( ++xe x++, respectivamente), é muito fácil introduzir erros sutis que são difíceis de rastrear.

FMM
fonte
13

De acordo com o código de bytes obtido dos arquivos de classe,

Ambas as atribuições aumentam x, mas a diferença é o tempo de when the value is pushed onto the stack

Em Case1, o Push ocorre (e depois é atribuído) antes do incremento (significando essencialmente que o incremento não faz nada)

Em Case2, o incremento ocorre primeiro (tornando-o 8) e depois empurrado para a pilha (e depois atribuído a x)

Caso 1:

int x=7;
x=x++;

Código de bytes:

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

Caso 2:

int x=7; 
x=++x;

Código de Byte

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
  • A pilha aqui se refere à pilha de operandos, local: x index: 1 type: int
evenprime
fonte
Você pode, por favor, explicar sua resposta em detalhes.
Nihar
Pls dê uma olhada no link e nos comentários
referenciados
8

É incrementado após " x = x++;". Seria 8 se você fizesse " x = ++x;".

FJ
fonte
4
Se ele é incrementado depois x = x++, então ele deve ser 8.
R. Martinho Fernandes
8

O operador Pós-incremento funciona da seguinte maneira:

  1. Armazene o valor anterior do operando.
  2. Incremente o valor do operando.
  3. Retorne o valor anterior do operando.

Então a afirmação

int x = 7;
x = x++; 

seria avaliado da seguinte forma:

  1. x é inicializado com o valor 7
  2. O operador pós incremento armazena o valor anterior de x, ou seja, 7 para retornar.
  3. Incrementa x, então agora x é 8
  4. Retorna o valor anterior de x, isto é, 7 e é atribuído de volta a x, então x novamente se torna 7

Portanto, x é realmente aumentado, mas como x ++ está atribuindo resultado de volta a x, o valor de x é substituído pelo seu valor anterior.

GauravLuthra
fonte
Mas no msvc x é 8. Sim no gcc e no clang x é 7. #
28718 Sun dom
7

O incremento ocorre depois que x é chamado, então x ainda é igual a 7. ++ x seria igual a 8 quando x é chamado

JonPM
fonte
7

Quando você atribui novamente o valor, xele ainda é 7. Tente x = ++xe você terá mais 8

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

porque x ++ incrementa o valor APÓS atribuí-lo à variável. assim por diante e durante a execução desta linha:

x++;

o varialbe x ainda terá o valor original (7), mas usando x novamente em outra linha, como

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

lhe dará 8.

se você quiser usar um valor incrementado de x na declaração de atribuição, use

++x;

Isso aumentará x em 1, ENTÃO atribua esse valor à variável x.

[Edit] em vez de x = x ++, é apenas x ++; o primeiro atribui o valor original de x a si próprio, portanto, na verdade, não faz nada nessa linha.

josephus
fonte
Aquele que diz que incrementa depois de atribuir, e aquele que diz que vai imprimir 8. incrementa antes de atribuir, e imprime 7.
R. Martinho Fernandes
se x for originalmente 7, System.out.println (String.valueOf (x ++)); impressões 7. você tem certeza de que estamos falando da mesma linguagem de programação?
Josephus
Sim, eu sou. Este ideone.com/kj2UU não imprime 8, como afirma a resposta.
R. Martinho Fernandes
sim, eu estava errado. x = x ++ atribuirá 7 a x primeiro antes de incrementar x. como x ++ (que é uma atribuição em si) resolve primeiro antes de x = (qualquer que seja), o valor atribuído a x em x = (qualquer que seja) será seguido. desculpe, eu não vi isso.
Josephus
1
Na verdade, o incremento é a primeira coisa que acontece. ideone.com/xOIDU
R. Martinho Fernandes
4

O que acontece quando int x = 7; x = x++;?

ans -> x++significa o primeiro valor de uso de x para expressão e depois aumente-o em 1.
É o que acontece no seu caso. O valor de x no RHS é copiado para a variável x no LHS e, em seguida, o valor de xé aumentado em 1.

Da mesma forma ++x , ->aumente o valor de x primeiro em um e depois use na expressão.
Portanto, no seu caso, x = ++x ; // where x = 7
você receberá um valor de 8.

Para maior clareza, tente descobrir quantas instruções printf executarão o seguinte código

while(i++ <5)   
  printf("%d" , ++i);   // This might clear your concept upto  great extend
samprat
fonte
não é correto "O valor de x em RHS é copiado para variável x em LHS e, em seguida, o valor de x é aumentado por 1" - isso faria xsendo 8, mas é 7 - incremento acontece entre ler e atribuição
user85421
3

++xé pré-incremento ->x é incrementado antes de ser usado
x++é pós-incremento ->x é incrementado após ser usado

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

Então isso significa: x++não é igual ax = x+1

Porque:

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

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

e agora parece um pouco estranho:

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

muito dependente do compilador!

linuxeasy
fonte
2
quem disse que era igual em primeiro lugar?
fortran 27/10
1
Se eu fosse você, jogaria fora esses livros imediatamente xD De qualquer forma, seria como (x = x + 1, x-1)em C, onde expressões separadas por vírgula são permitidas.
fortran
3
@fortran: Bem, na minha cópia de dez anos de "A linguagem de programação Java, terceira edição", na página 159, diz "" A expressão i ++ é equivalente a i = i + 1, exceto que eu sou avaliada apenas uma vez ". Quem disse em primeiro lugar? James Gosling, ao que parece. Esta parte desta edição da especificação Java é extraordinariamente vaga e mal especificada; presumo que edições posteriores tenham limpado a linguagem para expressar a semântica real do operador com mais clareza.
Eric Lippert
2
@ fortran: por "exceto i é avaliado apenas uma vez", o padrão está tentando transmitir que uma expressão como "M (). x ++" chama M () apenas uma vez. Uma redação menos vaga e mais precisa enfatizaria que existe uma diferença entre avaliar i como uma variável para determinar seu local de armazenamento - que é o que significa "avaliado apenas uma vez" aqui - e ler ou gravar nesse local de armazenamento - - uma das quais poderia ser uma interpretação razoável, mas incorreta, de 'avaliado'. Claramente, o local de armazenamento deve ser lido e gravado!
Eric Lippert
1
"muito dependente do compilador" - Nem um pouco!
Stephen C
-1

x = x ++;

Este é o operador pós-incremento. Deve ser entendido como "Use o valor do operando e depois aumente o operando".

Se você deseja que o inverso aconteça, ou seja, "Incremente o operando e use o valor do operando", você deve usar o operador de pré-incremento, como mostrado abaixo.

x = ++ x;

Esse operador primeiro incrementa o valor de x em 1 e depois atribui o valor de volta a x.

deepak
fonte
-1

Eu acho que essa controvérsia pode ser resolvida sem entrar em código e apenas pensar.

Considere i ++ e ++ i como funções, como Func1 e Func2.

Agora i = 7;
Func1 (i ++) retorna 7, Func2 (++ i) retorna 8 (todo mundo sabe disso). Internamente, ambas as funções aumentam i para 8, mas retornam valores diferentes.

Então i = i ++ chama a função Func1. Dentro da função, eu aumenta para 8, mas após a conclusão, a função retorna 7.

Então, finalmente, 7 é alocado para i. (Então, no final, i = 7)

Pranav Mahajan
fonte
2
Não há "controvérsia" válida aqui. O código se comporta de maneira demonstrável e o comportamento está em conformidade com o JLS. Quem pensa que se comporta de maneira diferente ou não tentou, ou está enganado. (Isso é um pouco como dizer que 7 x 7 é 49 é "controverso" quando alguém esquece suas tabelas de tempos ...)
Stephen C
-2

Isso ocorre porque você usou um operador pós-incremento. Nesta linha de código a seguir

x = x++;

O que acontece é que você está atribuindo o valor de x para x. x ++ incrementa x depois que o valor de x é atribuído a x. É assim que os operadores pós-incremento funcionam. Eles funcionam após a execução de uma instrução. Portanto, no seu código, x é retornado primeiro depois e depois é incrementado.

Se você fez

x = ++x;

A resposta seria 8 porque você usou o operador de pré-incremento. Isso incrementa o valor antes de retornar o valor de x.

Anon
fonte