C: Was ist der Unterschied zwischen ++ i und i ++?


888

Was ist in C der Unterschied zwischen der Verwendung von ++iund i++und welche sollte im Inkrementierungsblock einer forSchleife verwendet werden?


10
Ich bin nicht sicher, ob das Originalposter interessiert ist, aber in C ++ kann der Leistungsunterschied erheblich sein, da die Erstellung des temporären Objekts für einen benutzerdefinierten Typ teuer sein kann.
Am Freund

Antworten:


1100
  • ++ierhöht den Wert von iund gibt dann den inkrementierten Wert zurück.

     i = 1;
     j = ++i;
     (i is 2, j is 2)
    
  • i++erhöht den Wert von i, gibt jedoch den ursprünglichen Wert zurück, ider vor dem Inkrementieren gehalten wurde.

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

Für eine forSchleife funktioniert beides. ++ischeint häufiger zu sein, vielleicht weil dies in K & R verwendet wird .

Befolgen Sie auf jeden Fall die Richtlinie "lieber ++ials i++" und Sie werden nichts falsch machen.

Es gibt ein paar Kommentare zur Effizienz von ++iund i++. In jedem Nicht-Studenten-Projekt-Compiler gibt es keinen Leistungsunterschied. Sie können dies überprüfen, indem Sie sich den generierten Code ansehen, der identisch ist.

Die Effizienzfrage ist interessant ... hier ist mein Versuch, eine Antwort zu finden: Gibt es einen Leistungsunterschied zwischen i ++ und ++ i in C?

Wie @OnFreund feststellt, ist dies für ein C ++ - Objekt anders, da operator++()es sich um eine Funktion handelt und der Compiler nicht wissen kann, wie die Erstellung eines temporären Objekts für den Zwischenwert optimiert werden kann.


6
Wird dieser Effekt nicht auftreten, wenn die Schleife nach Erreichen der Endbedingung erneut ausgeführt wird? for(int i=0; i<10; i++){ print i; } Wird dies zum Beispiel nicht anders sein als for(int i=0; i<10; ++i){ print i; } Mein Verständnis ist, dass einige Sprachen je nach Verwendung unterschiedliche Ergebnisse liefern.
AVRTRAC

27
jonnyflash, beide funktionieren identisch, da sich das Inkrement von i und der Druck in unterschiedlichen Anweisungen befinden. Dies sollte für jede Sprache der Fall sein, die C-style ++ unterstützt. Der einzige Unterschied zwischen ++ i und i ++ besteht darin, dass der Wert der Operation in derselben Anweisung verwendet wird.
Mark Harrison

16
Da sie in den meisten Fällen identischen Code erzeugen, bevorzuge ich, i++weil er die Form "Operandenoperator" hat, a la eine Zuweisung "Operandenoperatorwert". Mit anderen Worten, der Zieloperand befindet sich auf der linken Seite des Ausdrucks, genau wie in einer Zuweisungsanweisung.
David R Tribble

2
@ MarkHarrison, es wird identisch funktionieren, nicht weil i++und print iin verschiedenen Anweisungen sind, sondern weil i++;und i<10sind. @ jonnyflashs Bemerkung ist nicht so falsch. Angenommen, Sie haben for(int i=0; i++<10){ print i; }und for(int i=0; ++i<10){ print i; }. Diese funktionieren anders als @johnnyflash im ersten Kommentar beschrieben.
Adam

3
@sam, weil in einer typischen for-Schleife im ++ i-Teil keine Nebenwirkung (z. B. Zuweisung) auftritt.
Mark Harrison

175

i ++ ist bekannt als Beitrag Erhöhungsschritte während ++ i genannt wird Pre - Schritte.

i++

i++ist nach dem Inkrementieren, da der iWert nach Abschluss der Operation um 1 erhöht wird.

Sehen wir uns das folgende Beispiel an:

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

Hier Wert von j = 1aber i = 2. Hier wird zuerst der Wert von izugewiesen und jdann ierhöht.

++i

++iist vor dem Inkrementieren, da der iWert vor der Operation um 1 erhöht wird . Es bedeutet, j = i;wird nach ausgeführt i++.

Sehen wir uns das folgende Beispiel an:

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

Hier Wert von j = 2aber i = 2. Hier iwird jnach dem i Inkrementieren von der Wert von zugewiesen i. Ebenso ++iwird vorher ausgeführt j=i;.

Für Ihre Frage, welche im Inkrementierungsblock einer for-Schleife verwendet werden soll? Die Antwort ist, Sie können jeden verwenden .. spielt keine Rolle. Es wird Ihre for-Schleife gleich Nr. Ausführen. von Zeiten.

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

Und

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

Beide Schleifen erzeugen die gleiche Ausgabe. dh 0 1 2 3 4.

Es ist nur wichtig, wo Sie es verwenden.

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

In diesem Fall wird ausgegeben 1 2 3 4 5.


1
Das Initialisieren von Variablen nach Präfix und Post-Fix hilft beim Verständnis. Vielen Dank.
Abdul Alim Shakir

42

Bitte machen Sie sich keine Sorgen über die "Effizienz" (Geschwindigkeit, wirklich), von der eine schneller ist. Wir haben heutzutage Compiler, die sich um diese Dinge kümmern. Verwenden Sie, was auch immer sinnvoll ist, basierend darauf, was Ihre Absicht klarer zeigt.


1
was, wie ich hoffe, bedeutet, Präfix (inc | dec) zu verwenden, es sei denn, Sie benötigen tatsächlich den alten Wert vor dem (inc | dec), was nur sehr wenige Menschen tun und dennoch einen verwirrenden Anteil der angeblichen Unterrichtsmaterialien verwenden. Schaffung eines Frachtkultes von Postfix-Nutzern, die nicht einmal wissen, was es ist '..!
underscore_d

Ich bin mir nicht sicher, ob "Compiler heutzutage ... sich um diese Dinge kümmern" allgemein wahr ist. Innerhalb einer benutzerdefinierten operator++(int)Version (der Postfix-Version) muss der Code so ziemlich eine temporäre Version erstellen, die zurückgegeben wird. Sind Sie sicher, dass Compiler dies jederzeit optimieren können?
Peter - Reinstate Monica

36

++i erhöht den Wert und gibt ihn dann zurück.

i++ Gibt den Wert zurück und erhöht ihn dann.

Es ist ein subtiler Unterschied.

Verwenden ++iSie für eine for-Schleife, da diese etwas schneller ist. i++erstellt eine zusätzliche Kopie, die einfach weggeworfen wird.


23
Mir ist kein Compiler bekannt, bei dem es zumindest für ganze Zahlen einen Unterschied macht.
blabla999

4
Es ist nicht schneller . Die Werte werden ignoriert (nur der Nebeneffekt ist wirksam) und der Compiler kann / wird genau den gleichen Code generieren.
Wildplasser

31

i++: In diesem Szenario wird zuerst der Wert zugewiesen und dann erfolgt die Inkrementierung.

++i: In diesem Szenario wird zuerst das Inkrement durchgeführt und dann der Wert zugewiesen

Unten ist die Bildvisualisierung und hier ist ein schönes praktisches Video , das dasselbe demonstriert.

Geben Sie hier die Bildbeschreibung ein


Wie kann man etwas nicht zugeordnetes inkrementieren?
Kouty

@kouty Sie können ein Register erhöhen, das keiner Variablen zugeordnet ist.
Polluks

20

Der Grund ++i kann etwas schneller sein als der i++, i++dass eine lokale Kopie des Werts von i erforderlich sein kann, bevor er inkrementiert wird, während dies ++iniemals der Fall ist. In einigen Fällen optimieren einige Compiler es, wenn möglich ... aber es ist nicht immer möglich, und nicht alle Compiler tun dies.

Ich versuche, mich nicht zu sehr auf Compiler-Optimierungen zu verlassen, daher folge ich dem Rat von Ryan Fox: Wenn ich beide verwenden kann, verwende ich ++i.


11
-1 für C ++ Antwort auf C Frage. Es gibt nicht mehr "lokale Kopie" des Werts von ials des Werts 1, wenn Sie eine Anweisung schreiben 1;.
R .. GitHub STOP HELPING ICE

14

Das effektive Ergebnis der Verwendung von entweder in einer Schleife ist identisch. Mit anderen Worten, die Schleife macht in beiden Fällen genau dasselbe.

In Bezug auf die Effizienz kann die Auswahl von i ++ gegenüber ++ i mit einer Strafe verbunden sein. In Bezug auf die Sprachspezifikation sollte die Verwendung des Post-Inkrement-Operators eine zusätzliche Kopie des Werts erstellen, auf den der Operator einwirkt. Dies könnte eine Quelle für zusätzliche Operationen sein.

Sie sollten jedoch zwei Hauptprobleme mit der vorhergehenden Logik berücksichtigen.

  1. Moderne Compiler sind großartig. Alle guten Compiler sind intelligent genug, um zu erkennen, dass in einer for-Schleife ein ganzzahliges Inkrement angezeigt wird, und beide Methoden werden auf denselben effizienten Code optimiert. Wenn die Verwendung von Post-Inkrement gegenüber Pre-Inkrement tatsächlich zu einer langsameren Laufzeit Ihres Programms führt, verwenden Sie einen schrecklichen Compiler.

  2. In Bezug auf die Komplexität der Betriebszeit sind die beiden Methoden (auch wenn tatsächlich eine Kopie ausgeführt wird) gleichwertig. Die Anzahl der Befehle, die innerhalb der Schleife ausgeführt werden, sollte die Anzahl der Operationen in der Inkrementierungsoperation erheblich dominieren. Daher wird in jeder Schleife von signifikanter Größe die Strafe der Inkrementierungsmethode durch die Ausführung des Schleifenkörpers massiv überschattet. Mit anderen Worten, Sie sollten sich viel besser Gedanken über die Optimierung des Codes in der Schleife machen als über das Inkrement.

Meiner Meinung nach läuft das ganze Thema einfach auf eine Stilpräferenz hinaus. Wenn Sie der Meinung sind, dass das Vorinkrement besser lesbar ist, verwenden Sie es. Persönlich bevorzuge ich das Post-Inkrement, aber das liegt wahrscheinlich daran, dass es mir beigebracht wurde, bevor ich etwas über Optimierung wusste.

Dies ist ein typisches Beispiel für vorzeitige Optimierung, und Probleme wie dieses können uns von schwerwiegenden Designproblemen ablenken. Es ist jedoch immer noch eine gute Frage, da es in "Best Practice" keine einheitliche Verwendung oder keinen einheitlichen Konsens gibt.


13

Beide erhöhen die Zahl. ++iist äquivalent zu i = i + 1.

i++und ++isind sehr ähnlich, aber nicht genau gleich. Beide erhöhen die Zahl, ++ierhöhen jedoch die Zahl, bevor der aktuelle Ausdruck ausgewertet wird, während i++die Zahl erhöht wird, nachdem der Ausdruck ausgewertet wurde.

Beispiel:

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

8

++i(Prefix Betrieb): Inkremente und dann den Wert zuweist
(z): int i = 5, int b = ++i In diesem Fall wird zunächst 6 bis b zugeordnet und inkrementiert dann bis 7 und so weiter.

i++(Postfix - Betrieb): Ordnet und dann den Wert inkrementiert
(z): int i = 5, int b = i++ In diesem Fall wird zunächst 5 bis b zugeordnet und inkrementiert dann bis 6 und so weiter.

Incase of for-Schleife: i++wird meistens verwendet, da normalerweise der Startwert von verwendet wird, ibevor die for-Schleife erhöht wird. Abhängig von Ihrer Programmlogik kann dies jedoch variieren.


7

++i: ist Pre-Inkrement, das andere ist Post-Inkrement.

i++: holt das Element und erhöht es dann.
++i: erhöht i und gibt dann das Element zurück.

Beispiel:

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

Ausgabe:

i: 0
i++: 0
++i: 2

5

Ich gehe davon aus, dass Sie den Unterschied in der Semantik jetzt verstehen (obwohl ich mich ehrlich frage, warum die Leute Fragen zum Stapelüberlauf stellen, anstatt zu lesen, wissen Sie, ein Buch oder ein Web-Tutorial oder so.

Ignorieren Sie jedoch die Leistungsfragen, die selbst in C ++ unwahrscheinlich sind. Dies ist das Prinzip, das Sie bei der Entscheidung verwenden sollten:

Sagen Sie, was Sie im Code meinen.

Wenn Sie den Wert vor dem Inkrement in Ihrer Anweisung nicht benötigen, verwenden Sie diese Form des Operators nicht. Es ist ein kleines Problem, aber wenn Sie nicht mit einem Styleguide arbeiten, der eine Version zugunsten der anderen verbietet (auch bekannt als knochenköpfiger Styleguide), sollten Sie das Formular verwenden, das am genauesten ausdrückt, was Sie tun möchten.

QED, verwenden Sie die Pre-Inkrement-Version:

for (int i = 0; i != X; ++i) ...

5

Der Unterschied kann durch diesen einfachen C ++ - Code unten verstanden werden:

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;

5

Der Hauptunterschied ist

  • i ++ Post ( nach Inkrementierung ) und
  • ++ i Pre ( vor dem Inkrementieren )

    • poste wenn i =1 die Schleife wie inkrementiert1,2,3,4,n
    • pre, wenn i =1 die Schleife wie inkrementiert2,3,4,5,n

5

i ++ und ++ i

Dieser kleine Code kann helfen, den Unterschied aus einem anderen Blickwinkel als die bereits veröffentlichten Antworten zu visualisieren:

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

Das Ergebnis ist:

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

Achten Sie auf die Vorher- und Nachher-Situationen.

für Schleife

Ich denke, dass eines davon in einem Inkrementierungsblock einer for-Schleife verwendet werden sollte. Das Beste, was wir tun können, um eine Entscheidung zu treffen, ist ein gutes Beispiel:

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

Das Ergebnis ist:

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

Ich weiß nichts über dich, aber ich sehe keinen Unterschied in der Verwendung, zumindest in einer for-Schleife.


5

Das folgende C-Code-Fragment veranschaulicht den Unterschied zwischen den Operatoren vor und nach dem Inkrementieren und Dekrementieren:

int  i;
int  j;

Inkrementoperatoren:

i = 1;
j = ++i;    // i is now 2, j is also 2
j = i++;    // i is now 3, j is 2

4

Vorkrementieren bedeutet Inkrementieren in derselben Zeile. Nachinkrement bedeutet Inkrement nach Ausführung der Zeile.

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

Wenn es um OR- UND AND-Operatoren geht, wird es interessanter.

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
}

In Array

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
}

In C ++ Post / Pre-Inkrement der Zeigervariable

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

4

In Kürze:

++iund i++funktioniert genauso, wenn Sie sie nicht in eine Funktion schreiben. Wenn Sie so etwas verwenden function(i++)oder sehen function(++i)Sie den Unterschied.

function(++i)sagt erstes Inkrement i um 1, danach setze dies iin die Funktion mit neuem Wert.

function(i++)sagt setzen zuerst iin die Funktion nach diesem Inkrement ium 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

2
Der Unterschied ist nicht wirklich an Funktionsaufrufe gebunden (und Sie können den Unterschied erkennen, ohne Funktionsaufrufe zu tätigen). Es gibt einen Unterschied zwischen int j = ++i;und int k = i++;selbst wenn kein Funktionsaufruf beteiligt ist.
Jonathan Leffler

3

Der einzige Unterschied besteht in der Reihenfolge der Operationen zwischen dem Inkrement der Variablen und dem vom Operator zurückgegebenen Wert.

Dieser Code und seine Ausgabe erklären den Unterschied:

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

Ausgabe ist:

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

++iGibt den Wert also grundsätzlich nach dem Inkrementieren zurück, während ++ider Wert vor dem Inkrementieren zurückgegeben wird. Am Ende wird in beiden Fällen der iWert des Willens erhöht.

Ein anderes Beispiel:

#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;
}

Ausgabe:

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

Oft gibt es keinen Unterschied

Unterschiede sind deutlich , wenn der zurückgegebene Wert einer anderen Variablen zugeordnet ist oder wenn das Inkrement in Verkettung mit anderen Operationen durchgeführt wird , in dem Operationen Vorrang angewendet wird ( i++*2verschieden von ++i*2, aber , (i++)*2und (++i)*2gibt den gleichen Wert) , in vielen Fällen sind sie untereinander austauschbar. Ein klassisches Beispiel ist die for-Schleifensyntax:

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

hat den gleichen Effekt von

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

Regel zum Erinnern

Um keine Verwechslung zwischen den beiden Operatoren zu machen, habe ich diese Regel übernommen:

Ordnen Sie die Position des Operators ++in Bezug auf die Variable ider Reihenfolge der ++Operation in Bezug auf die Zuordnung zu

Mit anderen Worten gesagt:

  • ++ vor i bedeutet, dass die Inkrementierung vor der Zuweisung durchgeführt werden muss ;
  • ++ After i bedeutet Inkrementierung muss nach Zuordnung durchgeführt werden :

3

Sie können sich die interne Konvertierung als mehrere Anweisungen vorstellen.

  • Fall 1
i++;

Sie können es denken als,

i;
i = i+1;
  • Fall 2
++i;

Sie können es denken als,

i = i+i;
i;

-3

a = i ++ bedeutet, dass a den aktuellen i-Wert enthält. a = ++ i bedeutet, dass a einen inkrementierten i-Wert enthält


10
Diese Antwort ist nicht korrekt. a = i++;bedeutet, dass der in gespeicherte Wert ader Wert ivor dem Inkrement ist, aber "ohne Inkrementieren" impliziert, dass inicht inkrementiert wird, was völlig falsch ist - iinkrementiert wird, aber der Wert des Ausdrucks der Wert vor dem Inkrement ist.
Jonathan Leffler

-6

Hier ist das Beispiel, um den Unterschied zu verstehen

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

Ausgabe: 10 12/11 11(abhängig von der Reihenfolge der Auswertung der Argumente für die printfFunktion, die je nach Compiler und Architektur variiert)

Erläuterung: i++-> iwird gedruckt und dann erhöht. (Druckt 10, wird aber izu 11) ++i-> iWert erhöht und druckt den Wert. (Druckt 12 und den Wert von iauch 12)


11
Dies führt zu undefiniertem Verhalten, da zwischen i++und++i
MM

@ Lundin ist das richtig, die LHS, RHS des Kommas haben einen Sequenzpunkt zwischen sich, aber die 2 Ausdrücke sind immer noch nicht sequenziert
Antti Haapala
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.