C: ¿Cuál es la diferencia entre ++ i e i ++?

889

En C, ¿cuál es la diferencia entre usar ++iy i++, y cuál debería usarse en el bloque de incremento de un forbucle?

The.Anti.9
fuente
10
No estoy seguro de que el póster original esté interesado, pero en C ++ la diferencia en el rendimiento puede ser sustancial, ya que la creación del objeto temporal puede ser costosa para un tipo definido por el usuario.
En Freund el

Respuestas:

1101
  • ++iincrementará el valor de i, y luego devolverá el valor incrementado.

     i = 1;
     j = ++i;
     (i is 2, j is 2)
    
  • i++incrementará el valor de i, pero devolverá el valor original que itenía antes de ser incrementado.

     i = 1;
     j = i++;
     (i is 2, j is 1)
    

Para un forbucle, cualquiera de los dos funciona. ++iparece más común, quizás porque eso es lo que se usa en K&R .

En cualquier caso, siga la directriz "preferir ++isobre i++" y no se equivocará.

Hay un par de comentarios sobre la eficiencia de ++iy i++. En cualquier compilador de proyectos que no sean estudiantes, no habrá diferencia de rendimiento. Puede verificar esto mirando el código generado, que será idéntico.

La pregunta de eficiencia es interesante ... aquí está mi intento de respuesta: ¿Hay una diferencia de rendimiento entre i ++ y ++ i en C?

Como señala @OnFreund , es diferente para un objeto C ++, ya que operator++()es una función y el compilador no puede saber cómo optimizar la creación de un objeto temporal para mantener el valor intermedio.

Mark Harrison
fuente
66
¿No afectará este efecto a que el ciclo se ejecute una vez más al llegar a la condición final? Por ejemplo, for(int i=0; i<10; i++){ print i; } esto no será diferente de lo que for(int i=0; i<10; ++i){ print i; } entiendo es que algunos idiomas le darán resultados diferentes dependiendo de lo que use.
aVeRTRAC
27
jonnyflash, ambos funcionarán de manera idéntica, ya que el incremento de i y la impresión están en declaraciones diferentes. Este debería ser el caso para cualquier lenguaje que soporte C-style ++. La única diferencia entre ++ i e i ++ será cuando se use el valor de la operación en la misma instrucción.
Mark Harrison
16
Como en la mayoría de los casos producen un código idéntico, prefiero i++porque es de la forma "operador-operador", como la asignación "valor-operador-operador". En otras palabras, el operando de destino está en el lado izquierdo de la expresión, al igual que en una declaración de asignación.
David R Tribble
2
@ MarkHarrison, funcionará de manera idéntica no porque i++y print iestán en declaraciones diferentes, sino porque i++;y i<10están. El comentario de @jonnyflash no está tan fuera de lugar. Supongamos que tienes for(int i=0; i++<10){ print i; }y for(int i=0; ++i<10){ print i; }. Estos funcionarán de manera diferente en la forma que @johnnyflash describió en el primer comentario.
Adam
3
@sam, porque en un bucle típico no hay efectos secundarios (por ejemplo, asignación) en la parte ++ i.
Mark Harrison
175

i ++ se conoce como Post Increment, mientras que ++ i se llama Pre Increment.

i++

i++es un incremento posterior porque incrementa iel valor de 1 en 1 después de que finaliza la operación.

Veamos el siguiente ejemplo:

int i = 1, j;
j = i++;

Aquí valor de j = 1but i = 2. Aquí el valor de ise asignará jprimero y luego ise incrementará.

++i

++ies un incremento previo porque incrementa iel valor en 1 antes de la operación. Significa que j = i;se ejecutará después i++.

Veamos el siguiente ejemplo:

int i = 1, j;
j = ++i;

Aquí valor de j = 2but i = 2. Aquí ise asignará el valor de jdespués del i incremento de i. Del mismo modo ++ise ejecutará antes j=i;.

Para su pregunta, ¿ cuál debe usarse en el bloque de incremento de un bucle for? la respuesta es, puedes usar cualquiera ... no importa. Ejecutará su for loop mismo no. de veces

for(i=0; i<5; i++)
   printf("%d ",i);

Y

for(i=0; i<5; ++i)
   printf("%d ",i);

Ambos bucles producirán la misma salida. es decir 0 1 2 3 4.

Solo importa dónde lo estés usando.

for(i = 0; i<5;)
    printf("%d ",++i);

En este caso la salida será 1 2 3 4 5.

Parag
fuente
1
Inicializar variables después del prefijo y la corrección posterior ayuda a comprender. Gracias.
Abdul Alim Shakir
42

No se preocupe por la "eficiencia" (velocidad, realmente) de cuál es más rápido. Tenemos compiladores en estos días que se encargan de estas cosas. Use el que tenga sentido para usar, según lo que muestre más claramente su intención.

Andy Lester
fuente
1
lo cual, espero, significa ' usar el prefijo (inc | dec) rement a menos que realmente necesite el valor anterior antes del (inc | dec), que muy pocas personas necesitan, y aún así una proporción desconcertante de supuestos materiales de enseñanza usan, creando un culto de carga de usuarios de postfix que ni siquiera saben lo que es '...!
underscore_d
No estoy seguro de que "los compiladores en estos días ... cuiden estas cosas" es universalmente cierto. Dentro de una costumbre operator++(int)(la versión postfix) el código tiene que crear un temporal que será devuelto. ¿Estás seguro de que los compiladores siempre pueden optimizar eso?
Peter - Restablece a Monica
36

++i incrementa el valor, luego lo devuelve.

i++ devuelve el valor y luego lo incrementa.

Es una sutil diferencia.

Para un bucle for, use ++i, ya que es un poco más rápido. i++creará una copia adicional que simplemente se descarta.

Ryan Fox
fuente
23
No conozco ningún compilador en el que al menos haga una diferencia para los enteros.
blabla999
44
No es mas rapido . Los valores se ignoran (solo el efecto secundario es efectivo) y el compilador puede / generará exactamente el mismo código.
wildplasser
31

i++: En este escenario, primero se asigna el valor y luego ocurre el incremento.

++i: En este escenario, primero se realiza el incremento y luego se asigna el valor

A continuación se muestra la visualización de la imagen y también aquí hay un bonito video práctico que demuestra lo mismo.

ingrese la descripción de la imagen aquí

Shivprasad Koirala
fuente
¿Cómo puedes incrementar algo no asignado?
kouty
@kouty Puede incrementar un registro no asignado a una variable.
Polluks
20

La razón ++i puede ser un poco más rápida de i++lo que i++puede requerir una copia local del valor de i antes de que se incremente, mientras que ++inunca lo hace. En algunos casos, algunos compiladores lo optimizarán si es posible ... pero no siempre es posible, y no todos los compiladores lo hacen.

Intento no confiar demasiado en las optimizaciones de los compiladores, así que seguiría el consejo de Ryan Fox: cuando puedo usar ambos, uso ++i.

OysterD
fuente
11
-1 para la respuesta de C ++ a la pregunta de C. No hay más "copia local" del valor de la ique hay del valor 1 cuando escribe una declaración 1;.
R .. GitHub DEJA DE AYUDAR AL HIELO
14

El resultado efectivo de usar cualquiera de los dos en un bucle es idéntico. En otras palabras, el bucle hará exactamente lo mismo en ambos casos.

En términos de eficiencia, podría haber una penalización al elegir i ++ sobre ++ i. En términos de la especificación del idioma, el uso del operador posterior al incremento debería crear una copia adicional del valor sobre el que actúa el operador. Esto podría ser una fuente de operaciones adicionales.

Sin embargo, debe considerar dos problemas principales con la lógica anterior.

  1. Los compiladores modernos son geniales. Todos los buenos compiladores son lo suficientemente inteligentes como para darse cuenta de que está viendo un incremento entero en un ciclo for, y optimizará ambos métodos al mismo código eficiente. Si el uso de post-incremento sobre pre-incremento realmente hace que su programa tenga un tiempo de ejecución más lento, entonces está utilizando un compilador terrible .

  2. En términos de complejidad de tiempo operacional, los dos métodos (incluso si se está realizando una copia) son equivalentes. El número de instrucciones que se realizan dentro del bucle debe dominar significativamente el número de operaciones en la operación de incremento. Por lo tanto, en cualquier bucle de tamaño significativo, la ejecución del cuerpo del bucle eclipsará enormemente la penalización del método de incremento. En otras palabras, es mucho mejor que te preocupes por optimizar el código en el bucle en lugar del incremento.

En mi opinión, todo el problema simplemente se reduce a una preferencia de estilo. Si crees que el pre-incremento es más legible, entonces úsalo. Personalmente, prefiero el post-incrment, pero eso es probablemente porque fue lo que me enseñaron antes de saber algo sobre la optimización.

Este es un ejemplo por excelencia de optimización prematura, y problemas como este tienen el potencial de distraernos de problemas serios en el diseño. Sin embargo, sigue siendo una buena pregunta, porque no hay uniformidad en el uso o consenso en la "mejor práctica".

dusktreader
fuente
13

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.

Ejemplo:

int i = 1;
int x = i++; //x is 1, i is 2
int y = ++i; //y is 3, i is 3
Usman
fuente
8

++i(Operación prefijo): Incrementos y entonces asigna el valor
(por ejemplo): int i = 5, int b = ++i En este caso, 6 se asigna a b primero y luego incrementos a 7 y así sucesivamente.

i++(Operación Postfix): asigna y luego incrementa el valor
(por ejemplo): int i = 5, int b = i++ En este caso, 5 se le asigna a b primero y luego incrementos a 6 y así sucesivamente.

Incase of for loop: i++se usa principalmente porque, normalmente, usamos el valor inicial de iantes de incrementar en for loop. Pero dependiendo de la lógica de su programa, puede variar.

Anands23
fuente
7

++i: es un incremento previo, el otro es un incremento posterior.

i++: obtiene el elemento y luego lo incrementa.
++i: incrementa iy luego devuelve el elemento.

Ejemplo:

int i = 0;
printf("i: %d\n", i);
printf("i++: %d\n", i++);
printf("++i: %d\n", ++i);

Salida:

i: 0
i++: 0
++i: 2
Scitech
fuente
5

Supongo que ahora comprende la diferencia en la semántica (aunque, sinceramente, me pregunto por qué la gente hace preguntas sobre "qué significa el operador X" en el desbordamiento de la pila en lugar de leer, ya sabes, un libro o un tutorial web o algo así.

Pero de todos modos, en cuanto a cuál usar, ignore las cuestiones de rendimiento, que probablemente no sean importantes incluso en C ++. Este es el principio que debe usar al decidir cuál usar:

Di lo que quieres decir en código.

Si no necesita el valor antes del incremento en su estado de cuenta, no use esa forma del operador. Es un problema menor, pero a menos que esté trabajando con una guía de estilo que prohíba una versión en favor de la otra (también conocida como una guía de estilo con cabeza de hueso), debe usar la forma que exprese lo que está tratando de hacer.

QED, use la versión previa al incremento:

for (int i = 0; i != X; ++i) ...
Scott Urban
fuente
5

La diferencia se puede entender con este simple código C ++ a continuación:

int i, j, k, l;
i = 1; //initialize int i with 1
j = i+1; //add 1 with i and set that as the value of j. i is still 1
k = i++; //k gets the current value of i, after that i is incremented. So here i is 2, but k is 1
l = ++i; // i is incremented first and then returned. So the value of i is 3 and so does l.
cout << i << ' ' << j << ' ' << k << ' '<< l << endl;
return 0;
IOstream
fuente
5

La principal diferencia es

  • i ++ Post ( después del incremento ) y
  • ++ i Pre ( antes del incremento )

    • publicar si i =1 el bucle se incrementa como1,2,3,4,n
    • pre si i =1 el bucle se incrementa como2,3,4,5,n
Gopinath Kaliappan
fuente
5

i ++ y ++ i

Este pequeño código puede ayudar a visualizar la diferencia desde un ángulo diferente al de las respuestas ya publicadas:

int i = 10, j = 10;

printf ("i is %i \n", i);
printf ("i++ is %i \n", i++);
printf ("i is %i \n\n", i);

printf ("j is %i \n", j);
printf ("++j is %i \n", ++j);
printf ("j is %i \n", j);

El resultado es:

//Remember that the values are i = 10, and j = 10

i is 10 
i++ is 10     //Assigns (print out), then increments
i is 11 

j is 10 
++j is 11    //Increments, then assigns (print out)
j is 11 

Presta atención a las situaciones de antes y después.

en bucle

En cuanto a cuál de ellos debe usarse en un bloque de incremento de un bucle for, creo que lo mejor que podemos hacer para tomar una decisión es usar un buen ejemplo:

int i, j;

for (i = 0; i <= 3; i++)
    printf (" > iteration #%i", i);

printf ("\n");

for (j = 0; j <= 3; ++j)
    printf (" > iteration #%i", j);

El resultado es:

> iteration #0 > iteration #1 > iteration #2 > iteration #3
> iteration #0 > iteration #1 > iteration #2 > iteration #3 

No sé sobre ti, pero no veo ninguna diferencia en su uso, al menos en un ciclo for.

carloswm85
fuente
5

El siguiente fragmento de código C ilustra la diferencia entre los operadores de incremento y decremento previos y posteriores:

int  i;
int  j;

Operadores de incremento:

i = 1;
j = ++i;    // i is now 2, j is also 2
j = i++;    // i is now 3, j is 2
Nihal Reddy
fuente
4

Precremento significa incremento en la misma línea. Post-incremento significa incremento después de que la línea se ejecuta.

int j=0;
System.out.println(j); //0
System.out.println(j++); //0. post-increment. It means after this line executes j increments.

int k=0;
System.out.println(k); //0
System.out.println(++k); //1. pre increment. It means it increments first and then the line executes

Cuando se trata de operadores OR, AND, se vuelve más interesante.

int m=0;
if((m == 0 || m++ == 0) && (m++ == 1)) { //false
/* in OR condition if first line is already true then compiler doesn't check the rest. It is technique of compiler optimization */
System.out.println("post-increment "+m);
}

int n=0;
if((n == 0 || n++ == 0) && (++n == 1)) { //true
System.out.println("pre-increment "+n); //1
}

En matriz

System.out.println("In Array");
int[] a = { 55, 11, 15, 20, 25 } ;
int ii, jj, kk = 1, mm;
ii = ++a[1]; // ii = 12. a[1] = a[1] + 1
System.out.println(a[1]); //12

jj = a[1]++; //12
System.out.println(a[1]); //a[1] = 13

mm = a[1];//13
System.out.printf ( "\n%d %d %d\n", ii, jj, mm ) ; //12, 12, 13

for (int val: a) {
     System.out.print(" " +val); //55, 13, 15, 20, 25
}

En C ++ post / pre-incremento de la variable puntero

#include <iostream>
using namespace std;

int main() {

    int x=10;
    int* p = &x;

    std::cout<<"address = "<<p<<"\n"; //prints address of x
    std::cout<<"address = "<<p<<"\n"; //prints (address of x) + sizeof(int)
    std::cout<<"address = "<<&x<<"\n"; //prints address of x

    std::cout<<"address = "<<++&x<<"\n"; //error. reference can't re-assign because it is fixed (immutable)
}
Uddhav Gautam
fuente
4

Dentro de poco:

++iy i++funciona igual si no los está escribiendo en una función. Si usa algo como function(i++)o function(++i)puede ver la diferencia.

function(++i)dice el primer incremento i por 1, después de eso pon esto ien la función con un nuevo valor.

function(i++)dice poner primero ien la función después de ese incremento ien 1.

int i=4;
printf("%d\n",pow(++i,2));//it prints 25 and i is 5 now
i=4;
printf("%d",pow(i++,2));//it prints 16 i is 5 now
GokhanAvci
fuente
2
La diferencia no está realmente vinculada a las llamadas a funciones (y puede detectar la diferencia sin hacer llamadas a funciones). Hay una diferencia entre int j = ++i;e int k = i++;incluso cuando no hay una llamada a la función involucrada.
Jonathan Leffler
3

La única diferencia es el orden de las operaciones entre el incremento de la variable y el valor que devuelve el operador.

Este código y su salida explican la diferencia:

#include<stdio.h>

int main(int argc, char* argv[])
{
  unsigned int i=0, a;
  a = i++;
  printf("i before: %d; value returned by i++: %d, i after: %d\n", i, a, i);
  i=0;
  a = ++i;
  printf("i before: %d; value returned by ++i: %d, i after: %d\n", i, a, i);
}

Salida es:

i before: 1; value returned by i++: 0, i after: 1
i before: 1; value returned by ++i: 1, i after: 1

Entonces, básicamente, ++idevuelve el valor después de que se incrementa, mientras que ++idevuelve el valor antes de que se incremente. Al final, en ambos casos ise incrementará su valor.

Otro ejemplo:

#include<stdio.h>

int main ()
  int i=0;
  int a = i++*2;
  printf("i=0, i++*2=%d\n", a);
  i=0;
  a = ++i * 2;
  printf("i=0, ++i*2=%d\n", a);
  i=0;
  a = (++i) * 2;
  printf("i=0, (++i)*2=%d\n", a);
  i=0;
  a = (++i) * 2;
  printf("i=0, (++i)*2=%d\n", a);
  return 0;
}

Salida:

i=0, i++*2=0
i=0, ++i*2=2
i=0, (++i)*2=2
i=0, (++i)*2=2

Muchas veces no hay diferencia

Las diferencias son claras cuando se asigna el valor devuelto a otra variable o cuando el incremento se realiza en concatenación con otras operaciones en que se aplica operaciones de precedencia ( i++*2es diferente de ++i*2, pero (i++)*2y (++i)*2devuelve el mismo valor) en muchos casos son intercambiables. Un ejemplo clásico es la sintaxis del bucle for:

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

tiene el mismo efecto de

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

Regla para recordar

Para no confundir a los dos operadores, adopté esta regla:

Asociar la posición del operador ++con respecto a la variable ial orden de la ++operación con respecto a la asignación

Dicho en otras palabras:

  • ++ antes i significa que el incremento debe realizarse antes de la asignación;
  • ++ después i significa que el incremento debe realizarse después de la asignación:
Francesco Boi
fuente
3

Puedes pensar en la conversión interna de eso como una declaración múltiple ;

  • caso 1
i++;

puedes pensarlo como

i;
i = i+1;
  • caso 2
++i;

puedes pensarlo como

i = i+i;
i;
Jeet Parikh
fuente
-3

a = i ++ significa que contiene un valor i actual a = ++ i significa que contiene un valor i incrementado

munna
fuente
10
Esta respuesta no es precisa. a = i++;significa que el valor almacenado aserá el valor ianterior al incremento, pero 'sin incrementar' implica que ino está incrementado, lo cual es completamente incorrecto, iestá incrementado, pero el valor de la expresión es el valor anterior al incremento.
Jonathan Leffler
-6

Aquí está el ejemplo para entender la diferencia.

int i=10;
printf("%d %d",i++,++i);

salida: 10 12/11 11(según el orden de evaluación de los argumentos de la printffunción, que varía según los compiladores y las arquitecturas)

Explicación: i++-> ise imprime y luego se incrementa. (Imprime 10, pero ise convertirá en 11) ++i-> el ivalor aumenta e imprime el valor. (Imprime 12, y el valor de itambién 12)

srinath
fuente
11
Esto provoca un comportamiento indefinido ya que no hay un punto de secuencia entre i++y++i
MM
@Lundin es así de correcto, sin embargo, el LHS, el RHS de la coma tienen un punto de secuencia entre ellos, pero las 2 expresiones aún no están secuenciadas entre sí
Antti Haapala