Diferença entre pré-incremento e pós-incremento em um loop?


303

Existe uma diferença dentro ++ie i++dentro de um forloop? É simplesmente uma coisa de sintaxe?



18
Estou impressionado com quantas respostas perderam completamente o objetivo da pergunta.
Graeme Perrow

3
Talvez devêssemos se surpreender que ninguém editado a questão para ser mais claro :)
Jon B

2
Esta questão poderia se aplicar a C, Java, C ++, PHP, C #, Javascript, JScript, Objective C: en.wikipedia.org/wiki/Category:C_programming_language_family
Chris S

1
Boa resposta postada aqui: stackoverflow.com/a/4706225/214296
Jim caiu

Respostas:


233

um ++ é conhecido como postfix.

adicione 1 a a, retorna o valor antigo.

++ a é conhecido como prefixo.

adicione 1 a a, retorna o novo valor.

C #:

string[] items = {"a","b","c","d"};
int i = 0;
foreach (string item in items)
{
    Console.WriteLine(++i);
}
Console.WriteLine("");

i = 0;
foreach (string item in items)
{
    Console.WriteLine(i++);
}

Resultado:

1
2
3
4

0
1
2
3

foreache whileloops dependem de qual tipo de incremento você usa. Com loops for como abaixo, não faz diferença, pois você não está usando o valor de retorno de i:

for (int i = 0; i < 5; i++) { Console.Write(i);}
Console.WriteLine("");
for (int i = 0; i < 5; ++i) { Console.Write(i); }

0 1 2 3 4
0 1 2 3 4

Se o valor avaliado for usado, o tipo de incremento se tornará significativo:

int n = 0;
for (int i = 0; n < 5; n = i++) { }

4
Isso nem é o que o usuário pediu.
Dimitri

223

O pré-incremento ++ i incrementa o valor de i e é avaliado para o novo valor incrementado.

int i = 3;
int preIncrementResult = ++i;
Assert( preIncrementResult == 4 );
Assert( i == 4 );

O i ++ pós-incremento incrementa o valor de i e é avaliado como o valor não incrementado original.

int i = 3;
int postIncrementResult = i++;
Assert( postIncrementtResult == 3 );
Assert( i == 4 );

Em C ++, o pré-incremento é geralmente preferido onde você pode usar qualquer um.

Isso ocorre porque, se você usar o pós-incremento, pode ser necessário que o compilador gere um código que crie uma variável temporária extra. Isso ocorre porque os valores anteriores e novos da variável que está sendo incrementada precisam ser mantidos em algum lugar, pois podem ser necessários em outro lugar na expressão que está sendo avaliada.

Portanto, pelo menos em C ++, pode haver uma diferença de desempenho que orienta sua escolha de qual usar.

Isso é principalmente apenas um problema quando a variável que está sendo incrementada é um tipo definido pelo usuário com um operador ++ substituído. Para tipos primitivos (int, etc), não há diferença de desempenho. Mas vale a pena aderir ao operador de pré-incremento como uma diretriz, a menos que o operador pós-incremento seja definitivamente o que é necessário.

Há mais discussões aqui:
https://web.archive.org/web/20170405054235/http://en.allexperts.com/q/C-1040/Increment-operators.htm

No C ++, se você estiver usando STL, poderá usar loops para iteradores. Eles têm principalmente operadores ++ substituídos, portanto, é recomendável seguir o pré-incremento. Porém, os compiladores ficam mais inteligentes o tempo todo, e os mais novos podem executar otimizações que significam que não há diferença de desempenho - especialmente se o tipo que está sendo incrementado for definido em linha no arquivo de cabeçalho (como as implementações STL geralmente são) para que o compilador possa ver como o método é implementado e pode saber quais otimizações são seguras para executar. Mesmo assim, provavelmente ainda vale a pena aderir ao pré-incremento, porque os loops são executados várias vezes e isso significa que uma pequena penalidade de desempenho poderá em breve ser ampliada.


Em outras linguagens como C #, onde o operador ++ não pode ser sobrecarregado, não há diferença de desempenho. Utilizados em um loop para avançar a variável do loop, os operadores de pré e pós incremento são equivalentes.

Correção: sobrecarregar ++ em C # é permitido. Parece, porém, que em comparação com o C ++, no c #, você não pode sobrecarregar as versões pré e pós independentemente. Portanto, suponho que, se o resultado da chamada de ++ em C # não for atribuído a uma variável ou usado como parte de uma expressão complexa, o compilador reduzirá as versões pré e pós do ++ para um código que tenha desempenho equivalente.


102
Não seria ótimo se C ++ fosse nomeado ++ C, indicando que você pode escrever um código bem otimizado usando-o ..
Naveen

9
Os compiladores modernos não deveriam otimizar isso quando o valor resultante for obviamente descartado?
che

6
@che - eles fazem quando é um tipo simples, no entanto, as classes que sobrecarregam o operador ++ (como iteradores) são uma história diferente.
Ferruccio

7
@che: Essa é uma boa pergunta. O motivo pelo qual os compiladores C ++ não substituem "CustomType ++;" com "++ CustomType;" é porque não há garantia de que ambas as funções definidas pelo usuário tenham o mesmo efeito. Eles devem ... mas não há garantia.
Tirou Dormann

2
@ michael.bartnett: Bom ponto, a sobrecarga do ++ em C # parece estar disponível. Parece que, comparado ao c ++, no c # você não pode sobrecarregar as versões pré e pós independentemente. Portanto, suponho que, se o resultado da chamada de ++ em C # não for atribuído a uma variável ou usado como parte de uma expressão complexa, o compilador reduzirá as versões pré e pós do ++ para um código que tenha desempenho equivalente.
Scott Langham

83

Em C # não há diferença quando usado em um loop for .

for (int i = 0; i < 10; i++) { Console.WriteLine(i); }

produz a mesma coisa que

for (int i = 0; i < 10; ++i) { Console.WriteLine(i); }

Como outros já apontaram, quando usado em geral i ++ e ++, tenho uma diferença sutil, mas significativa:

int i = 0;
Console.WriteLine(i++);   // Prints 0
int j = 0;
Console.WriteLine(++j);   // Prints 1

i ++ lê o valor de i e depois o incrementa.

++ i incrementa o valor de i e o lê.


Concluindo: a mesma semântica pós / pré-incremento que em C ++.
Xtofl

@xtofl - não sabe qual é o seu ponto? Acabei de escolher c # para o meu exemplo.
Jon B

3
Não acho que o primeiro ponto seja relevante. Em um loop for (c # ou não), a parte do incremento é sempre executada após o corpo do loop. Uma vez executada, a variável é modificada se pós ou pré-incremento foram usados.
precisa

9
@ MatthieuP - Eu li a pergunta como "importa se você usa i ++ ou ++ i em um loop for". A resposta é "não, não".
31909 Jon B

1
@ JonB A ordem das operações na resposta não está exatamente correta. Ambos ++ie i++executam as mesmas operações na mesma ordem: crie uma cópia temporária de i; aumente o valor do temp para produzir um novo valor (para não substituir o temp); armazene o novo valor em i; agora se é ++io resultado retornado é o novo valor; se i++o resultado retornado for a cópia temporária. Resposta mais detalhada aqui: stackoverflow.com/a/3346729/3330348
PiotrWolkowski

51

A questão é:

Existe uma diferença em ++ ie ++ em um loop for?

A resposta é: não .

Por que todas as outras respostas têm que entrar em explicações detalhadas sobre pré e pós incremento, quando isso nem sequer é solicitado?

Este loop for:

for (int i = 0; // Initialization
     i < 5;     // Condition
     i++)       // Increment
{
   Output(i);
}

Seria traduzir para este código sem usar loops:

int i = 0; // Initialization

loopStart:
if (i < 5) // Condition
{
   Output(i);

   i++ or ++i; // Increment

   goto loopStart;
}

Agora importa se você colocar i++ou ++icomo incremento aqui? Não, pois o valor de retorno da operação de incremento é insignificante. iserá incrementado APÓS a execução do código que está dentro do corpo do loop for.


2
Esta é literalmente a primeira resposta que vai direto ao ponto. Obrigado.
Yassir

1
Não é a melhor resposta, porque se o loop for estiver incrementando um objeto complexo (algo diferente de um int!), A implementação de ++ x pode ser mais rápida que x ++ ... (consulte herbsutter.com/2013/05/13/gotw -2-solution-temporary-objects )
JCx

30

Desde que você pergunta sobre a diferença em um loop, eu acho que você quer dizer

for(int i=0; i<10; i++) 
    ...;

Nesse caso, você não tem diferença na maioria dos idiomas: o loop se comporta da mesma maneira, independentemente de você escrever i++ou ++i. No C ++, você pode escrever suas próprias versões dos operadores ++ e definir significados separados para eles, se oi for de um tipo definido pelo usuário (sua própria classe, por exemplo).

A razão pela qual isso não importa acima é porque você não usa o valor de i++. Outra coisa é quando você faz

for(int i=0, a = 0; i<10; a = i++) 
    ...;

Agora, não é uma diferença, porque, como outros apontam, i++meios incrementar, mas avaliar ao valor anterior , mas ++imeios de incremento, mas avaliar ai (assim seria avaliar para o novo valor). No caso acima, aé atribuído o valor anterior de i, enquanto i é incrementado.


3
No C ++, nem sempre é possível que o compilador evite tornar o temporário; portanto, o formato de pré-incremento é preferido.
David Thornley

Enquanto escrevo, se você tem um i do tipo definido pelo usuário, eles podem ter semânticas diferentes. mas se você usar um i do tipo primitivo, isso não fará diferença para o primeiro loop. Como esta é uma questão independente da linguagem, achei que não escrevia muito sobre coisas específicas de C ++.
Johannes Schaub - litb 27/01

15

Como esse código mostra (consulte o MSIL desmembrado nos comentários), o compilador C # 3 não faz distinção entre i ++ e ++ i em um loop for. Se o valor de i ++ ou ++ i estivesse sendo obtido, definitivamente haveria uma diferença (isso foi compilado no Visutal Studio 2008 / Release Build):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PreOrPostIncrement
{
    class Program
    {
        static int SomethingToIncrement;

        static void Main(string[] args)
        {
            PreIncrement(1000);
            PostIncrement(1000);
            Console.WriteLine("SomethingToIncrement={0}", SomethingToIncrement);
        }

        static void PreIncrement(int count)
        {
            /*
            .method private hidebysig static void  PreIncrement(int32 count) cil managed
            {
              // Code size       25 (0x19)
              .maxstack  2
              .locals init ([0] int32 i)
              IL_0000:  ldc.i4.0
              IL_0001:  stloc.0
              IL_0002:  br.s       IL_0014
              IL_0004:  ldsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
              IL_0009:  ldc.i4.1
              IL_000a:  add
              IL_000b:  stsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
              IL_0010:  ldloc.0
              IL_0011:  ldc.i4.1
              IL_0012:  add
              IL_0013:  stloc.0
              IL_0014:  ldloc.0
              IL_0015:  ldarg.0
              IL_0016:  blt.s      IL_0004
              IL_0018:  ret
            } // end of method Program::PreIncrement             
             */
            for (int i = 0; i < count; ++i)
            {
                ++SomethingToIncrement;
            }
        }

        static void PostIncrement(int count)
        {
            /*
                .method private hidebysig static void  PostIncrement(int32 count) cil managed
                {
                  // Code size       25 (0x19)
                  .maxstack  2
                  .locals init ([0] int32 i)
                  IL_0000:  ldc.i4.0
                  IL_0001:  stloc.0
                  IL_0002:  br.s       IL_0014
                  IL_0004:  ldsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
                  IL_0009:  ldc.i4.1
                  IL_000a:  add
                  IL_000b:  stsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
                  IL_0010:  ldloc.0
                  IL_0011:  ldc.i4.1
                  IL_0012:  add
                  IL_0013:  stloc.0
                  IL_0014:  ldloc.0
                  IL_0015:  ldarg.0
                  IL_0016:  blt.s      IL_0004
                  IL_0018:  ret
                } // end of method Program::PostIncrement
             */
            for (int i = 0; i < count; i++)
            {
                SomethingToIncrement++;
            }
        }
    }
}

14

Um (++ i) é pré-incremento, um (i ++) é pós-incremento. A diferença está em qual valor é retornado imediatamente da expressão.

// Psuedocode
int i = 0;
print i++; // Prints 0
print i; // Prints 1
int j = 0;
print ++j; // Prints 1
print j; // Prints 1

Edit: Woops, ignorou completamente o lado do loop das coisas. Não há diferença real nos loops for quando é a parte 'step' (para (...; ...;)), mas pode entrar em jogo em outros casos.


7

Não há diferença se você não estiver usando o valor após o incremento no loop.

for (int i = 0; i < 4; ++i){
cout<<i;       
}
for (int i = 0; i < 4; i++){
cout<<i;       
}

Ambos os loops imprimirão 0123.

Mas a diferença ocorre quando você usa o valor após incremento / decremento no seu loop, conforme abaixo:

Loop de pré-incremento:

for (int i = 0,k=0; i < 4; k=++i){
cout<<i<<" ";       
cout<<k<<" "; 
}

Saída: 0 0 1 1 2 2 3 3

Pós-incremento do loop:

for (int i = 0, k=0; i < 4; k=i++){
cout<<i<<" ";       
cout<<k<<" "; 
}

Saída: 0 0 1 0 2 1 3 2

Espero que a diferença seja clara ao comparar a saída. O ponto a ser observado aqui é que o incremento / decremento é sempre executado no final do loop for e, portanto, os resultados podem ser explicados.


7

Aqui está uma amostra de Java e o código de bytes, pós e pré-incremento não mostram diferença no Bytecode:

public class PreOrPostIncrement {

    static int somethingToIncrement = 0;

    public static void main(String[] args) {
        final int rounds = 1000;
        postIncrement(rounds);
        preIncrement(rounds);
    }

    private static void postIncrement(final int rounds) {
        for (int i = 0; i < rounds; i++) {
            somethingToIncrement++;
        }
    }

    private static void preIncrement(final int rounds) {
        for (int i = 0; i < rounds; ++i) {
            ++somethingToIncrement;
        }
    }
}

E agora para o código de bytes (javap -private -c PreOrPostIncrement):

public class PreOrPostIncrement extends java.lang.Object{
static int somethingToIncrement;

static {};
Code:
0:  iconst_0
1:  putstatic   #10; //Field somethingToIncrement:I
4:  return

public PreOrPostIncrement();
Code:
0:  aload_0
1:  invokespecial   #15; //Method java/lang/Object."<init>":()V
4:  return

public static void main(java.lang.String[]);
Code:
0:  sipush  1000
3:  istore_1
4:  sipush  1000
7:  invokestatic    #21; //Method postIncrement:(I)V
10: sipush  1000
13: invokestatic    #25; //Method preIncrement:(I)V
16: return

private static void postIncrement(int);
Code:
0:  iconst_0
1:  istore_1
2:  goto    16
5:  getstatic   #10; //Field somethingToIncrement:I
8:  iconst_1
9:  iadd
10: putstatic   #10; //Field somethingToIncrement:I
13: iinc    1, 1
16: iload_1
17: iload_0
18: if_icmplt   5
21: return

private static void preIncrement(int);
Code:
0:  iconst_0
1:  istore_1
2:  goto    16
5:  getstatic   #10; //Field somethingToIncrement:I
8:  iconst_1
9:  iadd
10: putstatic   #10; //Field somethingToIncrement:I
13: iinc    1, 1
16: iload_1
17: iload_0
18: if_icmplt   5
21: return

}

5

Sim existe. A diferença está no valor de retorno. O valor de retorno de "++ i" será o valor após o incremento de i. O retorno de "i ++" será o valor antes do incremento. Isso significa que o código se parece com o seguinte:

int a = 0;
int b = ++a; // a is incremented and the result after incrementing is saved to b.
int c = a++; // a is incremented again and the result before incremening is saved to c.

Portanto, a seria 2 e bec seriam, cada um, 1.

Eu poderia reescrever o código assim:

int a = 0; 

// ++a;
a = a + 1; // incrementing first.
b = a; // setting second. 

// a++;
c = a; // setting first. 
a = a + 1; // incrementing second. 

4

Não há diferença real nos dois casos ' i' será incrementado em 1.

Mas há uma diferença quando você o usa em uma expressão, por exemplo:

int i = 1;
int a = ++i;
// i is incremented by one and then assigned to a.
// Both i and a are now 2.
int b = i++;
// i is assigned to b and then incremented by one.
// b is now 2, and i is now 3

3

Há mais em ++ ie i ++ que loops e diferenças de desempenho. ++ i retorna um valor l e i ++ retorna um valor r. Com base nisso, há muitas coisas que você pode fazer para (++ i), mas não para (i ++).

1- It is illegal to take the address of post increment result. Compiler won't even allow you.
2- Only constant references to post increment can exist, i.e., of the form const T&.
3- You cannot apply another post increment or decrement to the result of i++, i.e., there is no such thing as I++++. This would be parsed as ( i ++ ) ++ which is illegal.
4- When overloading pre-/post-increment and decrement operators, programmers are encouraged to define post- increment/decrement operators like:

T& operator ++ ( )
{
   // logical increment
   return *this;
}

const T operator ++ ( int )
{
    T temp( *this );
    ++*this;
    return temp;
}

3

Surpreende-me porque é que as pessoas podem escrever a expressão de incremento no loop for como i ++.

Em um loop for, quando o terceiro componente for uma instrução de incremento simples, como em

for (i=0; i<x; i++)  

ou

for (i=0; i<x; ++i)   

não há diferença nas execuções resultantes.


É uma resposta ou é uma pergunta?
Palec 17/10/16

2
Como isso não importa, por que você se incomodaria se alguém escrevesse i ++? Existe alguma razão para alguém preferir escrever ++ i?
Dronz

2

Como @ Jon B diz, não há diferença em um loop for.

Mas em um loop whileou do...while, você pode encontrar algumas diferenças se estiver fazendo uma comparação com o ++ioui++

while(i++ < 10) { ... } //compare then increment

while(++i < 10) { ... } //increment then compare

dois votos negativos? O que há de errado com o que escrevi? E está relacionado à pergunta (por mais vaga que seja).
crashmstr 27/01/09

2

Em javascript, devido ao seguinte i ++ pode ser melhor usar:

var i=1;
alert(i++); // before, 1. current, 1. after, 2.
alert(i); // before, 2. current, 2. after, 2.
alert(++i); // before, 2. current, 3 after, 3.

Enquanto matrizes (acho que todas) e algumas outras funções e chamadas usam 0 como ponto de partida, você teria que definir i como -1 para fazer o loop funcionar com a matriz ao usar ++ i .

Ao usar o i ++, o seguinte valor usará o valor aumentado. Você poderia dizer que o i ++ é o modo como os humanos contam, porque você pode começar com um 0 .


2

Para entender o que um loop FOR faz

insira a descrição da imagem aqui

A imagem acima mostra que FOR pode ser convertido em WHILE , pois eles acabam tendo o mesmo código de montagem (pelo menos em gcc). Assim, podemos dividir o FOR em duas partes, para entender o que ele faz.

for (i = 0; i < 5; ++i) {
  DoSomethingA();
  DoSomethingB();
}

é igual à versão WHILE

i = 0; //first argument (a statement) of for
while (i < 5 /*second argument (a condition) of for*/) {
  DoSomethingA();
  DoSomethingB();
  ++i; //third argument (another statement) of for
}

Isso significa que você pode usar FOR como uma versão simples do WHILE :

  1. O primeiro argumento de FOR (int i) é executado, fora, antes do loop.

  2. O terceiro argumento de FOR (i ++ ou ++ i) é executado, dentro, na última linha do loop.

TL: DR: não importa se i++ou ++i, sabemos que quando eles são autônomos, eles não fazem diferença, mas um em si mesmos.

Na escola, eles geralmente ensinam a maneira i ++, mas também há muitas pessoas que preferem a maneira ++ i devido a várias razões .

NOTA: No passado, o i ++ tinha muito pouco impacto no desempenho, pois não apenas adiciona um por si só, mas também mantém o valor original no registro. Mas, por enquanto, não faz diferença, pois o compilador torna a parte mais a mesma.


1

Pode haver uma diferença para loops. Esta é a aplicação prática de pós / pré-incremento.

        int i = 0;
        while(i++ <= 10) {
            Console.Write(i);
        }
        Console.Write(System.Environment.NewLine);

        i = 0;
        while(++i <= 10) {
            Console.Write(i);
        }
        Console.ReadLine();

Enquanto o primeiro conta até 11 e dá um loop 11 vezes, o segundo não.

Principalmente isso é usado em pouco tempo (x--> 0); - - Faça loop para iterar, por exemplo, todos os elementos de uma matriz (excluindo aqui todas as construções).


1

Ambos incrementam o número. ++ié equivalente a i = i + 1.

i++e ++isão muito parecidos, mas não exatamente iguais. Ambos incrementam o número, mas ++iincrementam o número antes que a expressão atual seja avaliada, enquanto que i++incrementam o número após a expressão ser avaliada.

int i = 3;
int a = i++; // a = 3, i = 4
int b = ++a; // b = 4, a = 

Verifique este link .


0

Sim, há uma diferença entre ++ie i++em um forloop, embora em casos de uso incomuns; quando uma variável de loop com operador de incremento / decremento é usada no bloco for ou dentro da expressão de teste de loop ou com uma das variáveis ​​de loop . Não, não é simplesmente uma coisa de sintaxe.

Como iem um código, significa avaliar a expressão ie o operador não significa uma avaliação, mas apenas uma operação;

  • ++isignifica valor de incremento de i1 e depois avalia i,
  • i++significa avaliar ie posteriormente incrementar o valor de i1.

Portanto, o que é obtido de cada duas expressões difere porque o que é avaliado difere em cada uma. Tudo igual para --iei--

Por exemplo;

let i = 0

i++ // evaluates to value of i, means evaluates to 0, later increments i by 1, i is now 1
0
i
1
++i // increments i by 1, i is now 2, later evaluates to value of i, means evaluates to 2
2
i
2

Em casos de uso incomuns, no entanto, o próximo exemplo parece útil ou não, não importa, mostra uma diferença

for(i=0, j=i; i<10; j=++i){
    console.log(j, i)
}

for(i=0, j=i; i<10; j=i++){
    console.log(j, i)
}

O que isso acrescenta às respostas existentes?
GManNickG 11/03

responde mais diretamente ao que é solicitado do que às respostas que li.
Selçuk

-2

Para itipos definidos pelo usuário, esses operadores podem (mas não devem ) terática semântica significativamente diferente no contexto de um índice de loop, e isso pode (mas não deve) afetar o comportamento do loop descrito.

Além disso, c++geralmente é mais seguro usar o formulário de pré-incremento ( ++i) porque é mais facilmente otimizado. (Scott Langham me venceu neste boato . Maldito seja, Scott)


A semântica do postfix deve ser maior que o prefixo. -1
xtofl 27/01/09

-2

Eu não sei para as outras linguagens, mas em Java ++ i é um incremento de prefixo, o que significa: aumente i em 1 e, em seguida, use o novo valor de i na expressão em que eu reside, e i ++ é um incremento postfix que significa o seguinte : use o valor atual de i na expressão e aumente-o em 1. Exemplo:

public static void main(String [] args){

    int a = 3;
    int b = 5;
    System.out.println(++a);
    System.out.println(b++);
    System.out.println(b);

} e a saída é:

  • 4
  • 5
  • 6

-3

i ++; ++ i; ambos são semelhantes, pois não são usados ​​em uma expressão.

class A {

     public static void main (String []args) {

     int j = 0 ;
     int k = 0 ;
     ++j;
     k++;
    System.out.println(k+" "+j);

}}

prints out :  1 1
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.