Was macht C-Entwickler so neugierig, wenn "i ++ == ++ i"? [geschlossen]

14

Nur eine zufällige Beobachtung, es scheint, dass es auf StackOverflow.com Fragen zu if "++ i == i ++" gibt. Diese Frage wird die ganze Zeit gestellt. Ich glaube, ich habe sie in den letzten zwei Monaten etwa sechs oder sieben Mal gestellt bekommen.

Ich frage mich nur, warum C-Entwickler sich so dafür interessieren. Dasselbe Konzept / dieselbe Frage gibt es auch für C # - und Java-Entwickler, aber ich glaube, ich habe nur eine C # -bezogene Frage gesehen.

Liegt es daran, dass so viele Beispiele ++ i verwenden? Liegt es daran, dass es ein beliebtes Buch oder Tutorial gibt? Liegt es daran, dass C-Entwickler es lieben, so viel wie möglich in eine einzige Zeile für 'Effizienz' / 'Leistung' zu packen und daher häufiger auf 'seltsame' Konstrukte mit dem ++ - Operator zu stoßen?

Michael Stum
quelle
23
(Für mich als C # -Entwickler sind i ++ und ++ gleich. Ich weiß, dass dies nicht der Fall ist. Wenn ich jedoch Code schreibe, der von den Unterschieden abhängt, würde ich ihn lieber umgestalten, damit er besser lesbar ist, und erwarten, dass sich der Compiler und JITter darum kümmern davon, aber als C # -Entwickler versuche ich nicht, ein oder zwei Taktzyklen zu speichern, weil ich sowieso schon relativ ineffizient bin)
Michael Stum
1
Als angehende Programmierer lieben wir es, mit Arithmetik und Logik zu spielen und sind neugierig, wie die Dinge funktionieren. Ich denke, das ist ein großer Grund, warum wir Noobs solche Fragen lieben. Es ist vielleicht kein wegweisendes Feature, aber es ist auf jeden Fall interessant! Ist das nicht der Grund, warum wir Programmierer geworden sind ?!
Chani
2
Beziehen Sie sich wörtlich auf den Ausdruck ++i == i++oder allgemeiner auf den Bedeutungsunterschied zwischen ++iund i++?
Keith Thompson

Antworten:

30

Ich vermute, dass zumindest ein Teil davon ein bisschen einfacher ist: Selbst jetzt sehen wir viele Fragen wie diese, die zu Beginn des Schuljahres anfangen und sich im Laufe des Jahres allmählich verjüngen.

Daher halte ich es für gerechtfertigt, zu vermuten, dass einige von ihnen einfach das Ergebnis von Klassen sind, in denen der Lehrer zumindest ein wenig darüber spricht, aber seine Punkte nicht sehr gut erklärt (so oft wie nicht) weil er sie selbst nicht wirklich versteht). Insbesondere aufgrund der Leute, die diese Fragen zu stellen scheinen, basieren nur wenige auf der tatsächlichen Codierung.

Jerry Sarg
quelle
14
Ist das nicht das Gegenteil vom Ewigen September? Im September begann AOL, seinen Kunden den Zugang zum Usenet zu ermöglichen, was den Anschein erweckte, dass der September (der Monat, in dem neue Schulanfänger traditionell die Boards als Neulinge bombardierten) das ganze Jahr über andauerte.
Nlawalker
Es ist eine interessante Theorie, aber vielleicht ist es nicht immer die Schuld des Lehrers, wenn jemand das Material nicht verstanden hat: Vielleicht waren die Schüler (1) nicht aufmerksam genug während des Unterrichts und (2) zu faul, um vorher nach einer Antwort zum Stapelüberlauf zu suchen die gleiche Frage noch einmal stellen.
Giorgio
12

Weil C-Programmierer die Reihenfolge der Operationen verstehen MÜSSEN. C # -Entwickler verwenden nicht unbedingt die bitweisen Operatoren (&, |, ~) oder Präfixoperatoren, da wir uns normalerweise mehr um andere Dinge kümmern. Was uns C # -Entwicklern jedoch nicht klar ist, ist, dass wir wissen sollten, was die von uns täglich verwendeten Operatoren tun und wie sie richtig eingesetzt werden. Die meisten von uns meiden nur die Stellen, an denen dies ein Problem sein kann.

Was ist die Ausgabe dieses Snippets?

    double x = 5.5;
    Console.WriteLine(x++);
    x = 5.5;
    Console.WriteLine(++x);

Ich würde sagen, dass viele erfahrene C # -Entwickler keine Ahnung haben, wie die Ausgabe an die Konsole lautet.

Nate Noonen
quelle
3
+1. In solchen Situationen lohnt es sich, den Unterschied zwischen den Inkrementierungs- / Dekrementierungsoperatoren vor und nach dem Fixieren zu kennen.
Maulrus
4
Ich verstehe den Punkt irgendwie und würde auch die Ausgabe nicht kennen (ich glaube, es ist 5.5 und 6.5, aber ich bin nicht sicher), und für mich wäre es egal, da ich es auf x ++ umgestalten würde; Console.WriteLine (x); sofort. Ich bin jedoch nicht völlig ahnungslos, ich denke nur, es ist ein großer Code-Geruch mit null Benutzerfreundlichkeit.
Michael Stum
8
Die Antwort auf die Frage hat nichts mit der Reihenfolge der Operationen oder dem Unterschied zwischen Präfix- und Postfix-Inkrementen zu tun. Es geht um definiertes und undefiniertes Verhalten.
David Thornley
2
Ich bin dem heute begegnet. So etwas wie if (myList[i++] == item). Klug. Ich fand es auf der Suche nach der Quelle einer IndexOutOfRange-Ausnahme ... In der Tat sehr "clever".
rmac
6
Ich denke, es ist 5.5 und 6.5, aber ich habe noch nie ++einen Schwimmer gesehen, und ich muss sagen, dass er sich nicht sehr angemessen anfühlt. Machen die Leute das? (Ich würde es vorziehen += 1)
Bart van Heukelom
8

Ich denke das ist offensichtlich. In C #, für ein int i, i++ == ++iist immer falsch , während ++i == i++immer wahr ist, zuverlässig, und jeder, der interessiert ist , dies aus , indem das Erlernen der Regeln von C # (oder tatsächlich durch nur läuft es) leicht nur finden können.

In C und C ++ ist es dagegen fast undefiniert, da es vom Compiler, der Ausführungsumgebung, der Plattform usw. abhängt. Daher ist es viel schwieriger, eine Frage zu beantworten, und viele Leute stellen sie.

Timwi
quelle
+1 - aber es ist nicht wirklich schwerer zu beantworten. "undefiniert" ist ein Wort. All das "es kommt darauf an ..." ist etwas, worüber sich die meisten Leute keine Sorgen machen. Selbst wenn Sie nur für eine bestimmte Plattform schreiben, sollten Sie die normalen Redewendungen für die Sprache als Ganzes verwenden, und die meisten Verwendungen von Operatoren vor und nach dem Inkrementieren sind Teil einiger gebräuchlicher Redewendungen.
Steve314
+1: Dies beantwortet tatsächlich die gestellte Frage (dh Sie lesen nicht zwischen den Zeilen).
Thomas Eding
5

Höchstwahrscheinlich, weil es in C wirklich wichtig ist, ob Sie schreiben i++oder ++iZeigerarithmetik ausführen. Dort kann eine Erhöhung ivor oder nach einer Operation entscheidend sein. Ich würde Ihnen ein Beispiel geben, aber das letzte Mal, dass ich C schrieb, war vor 10 Jahren ...

In C # bin ich nie auf eine Situation gestoßen, in der ich nachdenken müsste, i++oder ++iweil AFAIK für die for / while-Schleifen keine Rolle spielt.

Mladen Prajdic
quelle
3
Es ist immer noch wichtig, die Unterschiede in C # zu verstehen - nur weil Sie sie anscheinend nur für / jeden Loop verwenden, heißt das nicht, dass Sie sie nur an dieser Stelle finden werden
STW
das stimmt völlig.
Mladen Prajdic
3
Dies ist für die Frage nicht relevant. Bei der Frage geht es nicht um den Unterschied zwischen i++und ++i, sondern darum , sie in demselben Ausdruck zu kombinieren.
David Thornley
@ David Die Frage ist, warum C-Programmierer neugierig sind. Die Antwort (Es ist wichtig in C, nicht so sehr in C #) ist völlig relevant. "
Florian F
4

Es ist eine beliebte Frage, weil es eine Trickfrage ist. Es ist undefiniert .

Der obige Link führt zu einer FAQ auf der Homepage von Bjarnes Stroustrup, die sich speziell mit dieser Frage befasst und erklärt, warum dieses Konstrukt undefiniert ist. Was es sagt, ist:

Grundsätzlich ist in C und C ++ das Ergebnis undefiniert, wenn Sie eine Variable zweimal in einem Ausdruck lesen, in dem Sie sie auch schreiben.

Wenn die Reihenfolge der Auswertung undefiniert ist, wird behauptet, dass dies einen Code mit besserer Leistung ergibt.

user16764
quelle
2

Vor ein paar Jahren habe ich gelesen, dass diese Operatoren als gefährlich eingestuft wurden, also habe ich versucht, mehr Informationen darüber zu sammeln. Wenn zu diesem Zeitpunkt ein Stapelüberlauf aufgetreten wäre, hätte ich ihn dort gefragt.

Jetzt liegt es daran, dass einige verdrehte Leute Loops wie schreiben

while( [something] )
{
  a[++j] = ++j;
}

Selbst jetzt bin ich mir nicht sicher, was passieren wird / sollte, aber es ist mir ziemlich klar, dass mehrere ++ [var] in derselben Zeile nach Problemen fragen.

Eric
quelle
2
Inkremente haben ein undefiniertes Verhalten im Rahmen einer Zuweisung in C
tzenes
7
Zuweisungen wie x = ++j;in C sind gut definiert. Das Obige ist undefiniert, da jes zweimal ohne dazwischenliegende Sequenzpunkte modifiziert wird.
Matthew Flaschen
2

Zwei Gründe.

Die korrekte Implementierung von ++ i besteht darin, i zu erhöhen und dann zurückzugeben. Die korrekte Implementierung von i ++ besteht darin, den aktuellen Wert zu speichern, i zu erhöhen und den gespeicherten Wert zurückzugeben. Es ist wichtig zu wissen, dass diese nicht als Synonyme implementiert sind.

Die Frage wird dann, wann der Compiler sie anwendet, während er einen Ausdruck wie Gleichheit bewertet.

Wenn zuerst der Gleichheitstest durchgeführt wird, dann die Operatoren vor und nach dem Inkrementieren, müssen Sie eine andere Logik schreiben, als wenn zuerst die linke Seite (lhs) und die rechte Seite (rhs) ausgewertet werden die Gleichheit.

Es geht um die Reihenfolge der Operationen, und das wirkt sich wiederum auf den Code aus, den man schreibt. Und nicht allzu überraschend sind sich nicht alle Sprachen einig.

In der Liste der Basistests können Entwickler testen, ob ihre Annahmen korrekt sind und ob die tatsächliche Implementierung mit der Sprachspezifikation übereinstimmt.

Dies kann sich auf alle möglichen Arten auswirken, wenn Sie mit Zeigern, Schleifen oder der Rückgabe von Werten arbeiten.

Walt Steinbrenner
quelle
5
Grundlegende Tests zu diesem Thema sind irreführend, da es sich um undefiniertes Verhalten handelt. Wenn es genau so funktioniert, wie Sie es dieses Mal erwarten, gibt es keine Garantie, dass es das nächste Mal funktioniert.
David Thornley
Sie haben die falsche Frage beantwortet. Sie sollten antworten, warum C-Programmierer neugierig darauf sind.
Hugo
Ihr zweiter Absatz ist ohnehin falsch (zumindest vor C ++ 11): Das Verhalten von ++ibesteht darin , den Wert zu verwendeni+1 und zu einem bestimmten Zeitpunkt, vielleicht davor oder danach, aber vor dem nächsten Sequenzpunkt, zu erhöheni .
MM
@MattMcNabb - Können Sie mir eine Spezifikation dafür nennen? Ich bin gespannt, ob sich die Dinge in der Implementierung geändert haben. In den Tagen, in denen C der PDP-Assembly gut zugeordnet war, hat der ++ i eine INC-Anweisung injiziert, während die Suffix-Version den Wert in ein Register kopieren musste, um eine Anweisung nach dem Inkrement zu verwenden. Worin bestand die Notwendigkeit für die von Ihnen beschriebene Änderung?
Walt Stoneburner
2

Ich denke, es ist eine Kulturschocksache.

Wie bereits erwähnt, kann das Verhalten eines Ausdrucks in C oder C ++ undefiniert sein, da die Reihenfolge der Ausführung der Nachinkremente usw. nicht genau definiert ist. Undefiniertes Verhalten ist in C ziemlich verbreitet, und natürlich wurde ein Großteil davon in C ++ importiert.

Für jemanden, der in einer anderen Sprache programmiert hat - für jemanden, der die Idee aufgegriffen hat, dass Programmiersprachen präzise sein sollen und dass Computer das tun sollen, was sie sollen ... nun, das ist ein Schock für ihn entdecken Sie, dass C in einigen Dingen absichtlich vage ist.

Steve314
quelle
C wurde entwickelt, um wesentlich mehr Plattformen zu unterstützen, als es bei der Definition von C # überhaupt gab, einschließlich der seltsamsten Embedded- oder Mainframe-Plattformen, die Sie sich vorstellen können. C # hingegen läuft auf ... x86 und 360? vielleicht auch ARM? Nichts anderes. Deshalb kann C # viel mehr definieren.
DeadMG
++ Wurde C nicht zuerst auf einem PDP-11 gebaut? Wo gab es Auto-Inkrement-Anweisungen? C wollte "in der Nähe der Maschine" sein, um den Befehlssatz sinnvoll nutzen zu können.
Mike Dunlavey
@MikeDunlavey: Nein, Cs ++und --Operatoren basierten nicht auf dem PDP-11, der nicht existierte, als diese Operatoren in Cs Vorgängersprache B eingeführt wurden. Siehe cm.bell-labs.com/who/dmr/chist.html und suche nach "auto-increment".
Keith Thompson
1

Vielleicht ist es nur so, dass C-Entwickler ++ - Inkrementierung im Allgemeinen häufiger verwenden - da C keine "foreach" - oder ähnliche Anweisung zum Durchlaufen von Arrays hat. Oh, und natürlich Zeigerarithmus, wie schon erwähnt!

Robert Roos
quelle
1
Guter Punkt, vergessen, dass foreach ein relativ modernes Konzept ist.
Michael Stum
-1

Der Unterschied zwischen beiden Teilen: Post und Preincrement funktionieren so, wie diese beiden Codeteile in jeder Sprache funktionieren sollten. DAS IST NICHT SPRACHENABHÄNGIG !!!

++i

Dies ist ein Vorschritt. Dieser Code erhöht zuerst den Wert von, ibevor er den Wert an die Zeile sendet, in der er verwendet wird.

i++

Dies ist das Postinkrement. Dieser Code gibt den Wert von zurück, ibevor er den Wert iin der Zeile erhöht, in der er verwendet wird.

In einem anderen kleinen Beispiel:

int main(void)
{
  int i = 0; 
  int result; 
  result = 10 + ++i; 
  printf("%d \n", result); // = 11 

  int j = 0; 
  result = 10 + j++; 
  printf("%d \n", result);// = 10
  return 0;
}

Dieser Code wird mit den Ergebnissen auf dem Codepad kompiliert.

Dies hat mit der internen Implementierung der Operatorüberladung zu tun.

++i erhöht direkt den Wert (und ist daher etwas schneller)

i++Zunächst wird eine Kopie erstellt, die an die Stelle zurückgegeben wird, an der sie benötigt wird. Anschließend wird der ursprüngliche Wert der iVariablen um eins erhöht.

Ich hoffe das ist jetzt klar.

Krie999
quelle
Dies spricht jedoch nicht die eigentliche Frage an: Warum fragen C-Entwickler diesbezüglich mehr als andere. Das OP versteht die Bediener.
Martijn Pieters
Auch diese Antwort ist sachlich falsch. ++iist nicht schneller und erstellt i++keine Kopie. Außerdem ++iverhält es sich in C etwas anders als in C ++, geschweige denn zwischen anderen Sprachen.
MM