¿Diferencia entre pre-incremento y post-incremento en un ciclo?


303

¿Hay alguna diferencia en ++iy i++en un forbucle? ¿Es simplemente una cosa de sintaxis?



18
Estoy sorprendido de cuántas respuestas perdieron por completo el punto de la pregunta.
Graeme Perrow el

3
Quizás deberíamos sorprendernos de que nadie haya editado la pregunta para que sea más clara :)
Jon B

2
Esta pregunta podría aplicarse a C, Java, C ++, PHP, C #, Javascript, JScript, Objetivo C: en.wikipedia.org/wiki/Category:C_programming_language_family
Chris S

1
Buena respuesta publicada aquí: stackoverflow.com/a/4706225/214296
Jim Fell

Respuestas:


233

a ++ se conoce como postfix.

agregue 1 a a, devuelve el valor anterior.

++ a se conoce como prefijo.

agregue 1 a a, devuelve el nuevo 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++);
}

Salida:

1
2
3
4

0
1
2
3

foreachy los whilebucles dependen del tipo de incremento que use. Con los bucles for como a continuación, no hay diferencia ya que no está utilizando el 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

Si se utiliza el valor evaluado, el tipo de incremento se vuelve significativo:

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

44
Esto ni siquiera es lo que pidió el usuario.
Dimitri

224

Pre-incremento ++ i incrementa el valor de i y evalúa el nuevo valor incrementado.

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

Post-incremento i ++ incrementa el valor de i y evalúa el valor original no incrementado.

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

En C ++, generalmente se prefiere el preincremento donde puede usar cualquiera.

Esto se debe a que si usa el incremento posterior, puede requerir que el compilador tenga que generar código que cree una variable temporal adicional. Esto se debe a que tanto los valores anteriores como los nuevos de la variable que se incrementa deben mantenerse en algún lugar porque pueden ser necesarios en otro lugar de la expresión que se está evaluando.

Entonces, al menos en C ++, puede haber una diferencia de rendimiento que guíe su elección de cuál usar.

Esto es principalmente solo un problema cuando la variable que se incrementa es un tipo definido por el usuario con un operador ++ anulado. Para los tipos primitivos (int, etc.) no hay diferencia de rendimiento. Pero vale la pena apegarse al operador anterior al incremento como guía a menos que el operador posterior al incremento sea definitivamente lo que se requiere.

Aquí hay más discusión:
https://web.archive.org/web/20170405054235/http://en.allexperts.com/q/C-1040/Increment-operators.htm

En C ++ si está usando STL, entonces puede estar usando bucles con iteradores. Estos tienen principalmente operadores anulados ++, por lo que es una buena idea atenerse al pre-incremento. Sin embargo, los compiladores se vuelven más inteligentes todo el tiempo, y los más nuevos pueden realizar optimizaciones que significan que no hay diferencia de rendimiento, especialmente si el tipo que se incrementa se define en línea en el archivo de encabezado (como lo son a menudo las implementaciones de STL) para que el compilador pueda ver cómo el método se implementa y luego puede saber qué optimizaciones son seguras de realizar. Aun así, probablemente valga la pena seguir con el pre-incremento porque los bucles se ejecutan muchas veces y esto significa que una pequeña penalización de rendimiento pronto podría amplificarse.


En otros lenguajes como C #, donde el operador ++ no se puede sobrecargar, no hay diferencia de rendimiento. Utilizado en un bucle para avanzar la variable del bucle, los operadores de incremento previo y posterior son equivalentes.

Corrección: se permite la sobrecarga de ++ en C #. Sin embargo, parece que, en comparación con C ++, en C # no puede sobrecargar las versiones pre y post de forma independiente. Por lo tanto, supongo que si el resultado de llamar a ++ en C # no se asigna a una variable o no se usa como parte de una expresión compleja, el compilador reduciría las versiones anteriores y posteriores de ++ a un código que funcione de manera equivalente.


102
¿No habría sido grande si C ++ fue nombrado ++ C que indica que puede escribir un código bien optimizado usarlo ..
Naveen

99
¿No deberían los compiladores modernos poder optimizar esto cuando el valor resultante obviamente se va a la basura de todos modos?
che

66
@che: lo hacen cuando es un tipo simple, sin embargo, las clases que sobrecargan el operador ++ (como los iteradores) son una historia diferente.
Ferruccio

77
@che: Esa es una buena pregunta. La razón por la que los compiladores de C ++ no reemplazan "CustomType ++;" con "++ CustomType;" es porque no hay garantía de que ambas funciones definidas por el usuario tengan el mismo efecto. DEBERÍAN ... pero no hay garantía.
Drew Dormann

2
@ michael.bartnett: Buen punto, la sobrecarga de ++ en C # parece estar disponible. Sin embargo, parece que, en comparación con c ++, en c # no puede sobrecargar las versiones pre y post de forma independiente. Por lo tanto, supongo que si el resultado de llamar a ++ en C # no se asigna a una variable o no se usa como parte de una expresión compleja, el compilador reduciría las versiones pre y post de ++ a un código que funcione de manera equivalente.
Scott Langham

83

En C # no hay diferencia cuando se usa en un bucle for .

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

produce lo mismo que

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

Como otros han señalado, cuando se usa en general i ++ y ++ tengo una diferencia sutil pero significativa:

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

i ++ lee el valor de i y luego lo incrementa.

++ i incrementa el valor de i y luego lo lee.


Concluyendo: la misma semántica post / pre incremento que en C ++.
xtofl

@xtofl: ¿no estás seguro de cuál es tu punto? Acabo de elegir C # para mi ejemplo.
Jon B

3
No creo que el primer punto sea relevante. En un bucle for (c # o no), la parte de incremento siempre se ejecuta después del cuerpo del bucle. Una vez ejecutada, la variable se modifica si se utilizó post o pre incremento.
MatthieuP

99
@MatthieuP - Leí la pregunta como "¿importa si usas i ++ o ++ i en un bucle for". La respuesta es "no, no lo hace".
Jon B

1
@ JonB El orden de las operaciones en la respuesta no es exactamente correcto. Ambos ++iy i++realizan las mismas operaciones en el mismo orden: crear una copia temporal de i; incremente el valor temporal para producir un nuevo valor (no para anular la temperatura); almacenar el nuevo valor en i; ahora si es ++iel resultado devuelto es el nuevo valor; si es i++el resultado devuelto es la copia temporal. Respuesta más detallada aquí: stackoverflow.com/a/3346729/3330348
PiotrWolkowski

51

La pregunta es:

¿Hay alguna diferencia en ++ i e i ++ en un bucle for?

La respuesta es: no .

¿Por qué todas y cada una de las respuestas tienen que entrar en explicaciones detalladas sobre el incremento previo y posterior cuando esto ni siquiera se pregunta?

Este bucle for:

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

Se traduciría a este código sin usar bucles:

int i = 0; // Initialization

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

   i++ or ++i; // Increment

   goto loopStart;
}

¿Ahora importa si pones i++o ++icomo incremento aquí? No, no lo hace, ya que el valor de retorno de la operación de incremento es insignificante. ise incrementará DESPUÉS de la ejecución del código que está dentro del cuerpo del bucle for.


2
Esta es literalmente la primera respuesta que va directamente al grano. Gracias.
Yassir

1
No es la mejor respuesta porque si el bucle for está incrementando un objeto complejo (¡algo que no sea un int!), La implementación de ++ x puede ser más rápida que x ++ ... (ver herbalutter.com/2013/05/13/gotw -2-solución-objetos-temporales )
JCx

30

Como preguntas sobre la diferencia en un bucle, supongo que te refieres

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

En ese caso, no tiene diferencia en la mayoría de los idiomas: el bucle se comporta igual independientemente de si escribe i++y ++i. En C ++, puede escribir sus propias versiones de los operadores ++, y puede definir significados separados para ellos, si eli es de un tipo definido por el usuario (su propia clase, por ejemplo).

La razón por la que no importa más arriba es porque no utiliza el valor de i++. Otra cosa es cuando lo haces

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

Ahora bien, no es una diferencia, porque como otros señalan, i++medios de incremento, pero se evalúan como el valor anterior , pero ++ilos medios de incremento, pero a evaluari (por lo que se evaluará al nuevo valor). En el caso anterior, ase le asigna el valor anterior de i, mientras que i se incrementa.


3
En C ++, no siempre es posible que el compilador evite hacer el temporal, por lo que se prefiere la forma de preincremento.
David Thornley

Mientras escribo, si tiene una i de tipo definido por el usuario, podrían tener una semántica diferente. pero si usa una i de tipo primitivo, entonces no hay diferencia para el primer bucle. Como esta es una pregunta independiente del lenguaje, pensé en no escribir demasiado sobre cosas específicas de C ++.
Johannes Schaub - litb

15

Como muestra este código (vea el MSIL desmontado en los comentarios), el compilador C # 3 no hace distinción entre i ++ y ++ i en un bucle for. Si se tomara el valor de i ++ o ++, definitivamente habría una diferencia (esto se compiló en 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

Uno (++ i) es preincremento, uno (i ++) es postincremento. La diferencia está en qué valor se devuelve inmediatamente de la expresión.

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

Editar: Woops, ignoró por completo el lado del bucle de las cosas. No hay una diferencia real en los bucles for cuando es la parte de 'paso' (para (...; ...;)), pero puede entrar en juego en otros casos.


7

No hay diferencia si no está utilizando el valor después del incremento en el bucle.

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

Ambos bucles imprimirán 0123.

Pero la diferencia viene cuando usa el valor después del incremento / decremento en su ciclo de la siguiente manera:

Bucle Pre Incremento:

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

Salida: 0 0 1 1 2 2 3 3

Post Increment Loop:

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

Salida: 0 0 1 0 2 1 3 2

Espero que la diferencia sea clara al comparar la salida. El punto a tener en cuenta aquí es que el incremento / decremento siempre se realiza al final del ciclo for y, por lo tanto, los resultados se pueden explicar.


7

Aquí hay una muestra de Java y el código de byte, post- y preIncrement no muestran diferencias en 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;
        }
    }
}

Y ahora para el 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

Sí hay. La diferencia está en el valor de retorno. El valor de retorno de "++ i" será el valor después de incrementar i. El retorno de "i ++" será el valor antes de incrementar. Esto significa ese código que se parece a lo siguiente:

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.

Por lo tanto, a sería 2, y byc serían cada uno 1.

Podría reescribir el código así:

int a = 0; 

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

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

4

No hay diferencia real en ambos casos ' i' se incrementará en 1.

Pero hay una diferencia cuando lo usa en una expresión, por ejemplo:

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

Hay más en ++ i e i ++ que los bucles y las diferencias de rendimiento. ++ i devuelve un valor l e i ++ devuelve un valor r. En base a esto, hay muchas cosas que puede hacer para (++ i) pero no 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

Me desconcierta por qué las personas pueden escribir la expresión de incremento en for-loop como i ++.

En un ciclo for, cuando el tercer componente es una declaración de incremento simple, como en

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

o

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

No hay diferencia en las ejecuciones resultantes.


¿Es una respuesta o es una pregunta?
Palec

2
Como no importa, ¿por qué te aturdiría si alguien escribiera i ++? ¿Hay alguna razón por la cual alguien preferiría escribir ++ i?
Dronz

2

Como dice @Jon B , no hay diferencia en un bucle for.

Pero en un bucle whileo do...while, podría encontrar algunas diferencias si está haciendo una comparación con el ++ioi++

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

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

dos votos negativos? ¿Qué hay de malo con lo que escribí? Y está relacionado con la pregunta (tan vago como es).
crashmstr

2

En javascript debido a lo siguiente, i ++ puede ser mejor para 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.

Mientras que las matrices (creo que todas) y algunas otras funciones y llamadas usan 0 como punto de partida, tendría que establecer i en -1 para que el bucle funcione con la matriz cuando se usa ++ i .

Al usar i ++, el siguiente valor usará el valor aumentado. Podrías decir que i ++ es la forma en que los humanos cuentan, porque puedes comenzar con un 0 .


2

Para entender lo que hace un bucle FOR

ingrese la descripción de la imagen aquí

La imagen de arriba muestra que FOR se puede convertir a WHILE , ya que finalmente tienen el mismo código de ensamblaje (al menos en gcc). Entonces podemos dividir FOR en un par de piezas, para comprender lo que hace.

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

es igual a la versión MIENTRAS

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
}

Significa que puede usar FOR como una versión simple de WHILE :

  1. El primer argumento de FOR (int i) se ejecuta, fuera, antes del bucle.

  2. El tercer argumento de FOR (i ++ o ++ i) se ejecuta, dentro, en la última línea del bucle.

TL: DR: no importa si i++o++i , sabemos que cuando son independientes, no hacen diferencia sino +1 en sí mismos.

En la escuela, generalmente enseñan la forma i ++, pero también hay muchas personas que prefieren la forma ++ i debido a varias razones .

NOTA: En el pasado, i ++ tiene muy poco impacto en el rendimiento, ya que no solo suma uno por sí mismo, sino que también mantiene el valor original en el registro. Pero por ahora, no hace ninguna diferencia, ya que el compilador hace que la parte plus sea igual.


1

Puede haber una diferencia para los bucles. Esta es la aplicación práctica de post / pre-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();

Mientras que el primero cuenta hasta 11 y se repite 11 veces, el segundo no.

Principalmente, esto se usa bastante en un tiempo simple (x--> 0); - - Bucle para iterar, por ejemplo, todos los elementos de una matriz (exceptuando las construcciones foreach aquí).


1

Ambos incrementan el número. ++ies equivalente a i = i + 1.

i++y ++ison muy similares pero no exactamente iguales. Ambos incrementan el número, pero ++iincrementan el número antes de que se evalúe la expresión actual, mientras que i++incrementa el número después de que se evalúa la expresión.

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

Mira este enlace .


0

Sí, hay una diferencia entre ++iy i++en un forbucle, aunque en casos de uso inusuales; cuando se usa una variable de bucle con operador de incremento / decremento en el bloque for o dentro de la expresión de prueba de bucle , o con una de las variables de bucle . No, no es simplemente una cuestión de sintaxis.

Como ien un código significa evaluar la expresión iy el operador no significa una evaluación sino solo una operación;

  • ++isignifica incrementar el valor ien 1 y luego evaluar i,
  • i++significa evaluar iy luego incrementar el valor ien 1.

Entonces, lo que se obtiene de cada dos expresiones difiere porque lo que se evalúa difiere en cada una. Todo lo mismo para --iyi--

Por ejemplo;

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

En casos de uso inusual, sin embargo, el siguiente ejemplo suena útil o no importa, muestra una diferencia

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)
}

¿Qué agrega esto sobre las respuestas existentes?
GManNickG

responde más directamente lo que se pregunta que las respuestas que he leído.
Selçuk

-2

Para ilos tipos definidos por el usuario, estos operadores podrían (pero no deberían ) tener semánticas significativamente diferentes en el contexto de un índice de bucle, y esto podría (pero no debería) afectar el comportamiento del bucle descrito.

Además, en c++general es más seguro usar el formulario de pre-incremento ( ++i) porque se optimiza más fácilmente. (Scott Langham me ganó en este bocado . Maldito seas, Scott)


Se supone que la semántica de postfix es mayor que el prefijo. -1
xtofl

-2

No sé para los demás idiomas, pero en Java ++ i es un prefijo de la subasta , que significa: incrementar i en 1 y luego usar el nuevo valor de i en la expresión en la que i reside, y i ++ es un incremento de sufijo que significa lo siguiente : use el valor actual de i en la expresión y luego aumente en 1. Ejemplo:

public static void main(String [] args){

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

} y el resultado es:

  • 4 4
  • 5 5
  • 6 6

-3

i ++; ++ i; ambos son similares ya que no se usan en una expresión.

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.