+ (+ k--) Ausdruck in C.

9

Ich habe diese Frage in einem Test gesehen, in dem wir die Ausgabe des folgenden Codes mitteilen müssen.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

Die Ausgabe ist -1. Ich bin mir jedoch nicht sicher, warum dies die Antwort ist.

Was bedeutet der Ausdruck +(+k--)in C?

Ankur Gautam
quelle
4
Wurde es so formatiert? Das ist gemein ;-)
Peter - Reinstate Monica
3
Hinweis: Schreiben Sie niemals BS-Code wie diesen im wirklichen Leben.
Jabberwocky
5
Dies ist nicht UB. Siehe meine Antwort.
dbush
@ Peter-ReinstateMonica Ja, es wurde so formatiert.
Ankur Gautam
2
Aufgrund seiner erfundenen Natur und bizarren Wendungen - k=k++ist undefiniert, aber nicht undefiniert, weil es aufgrund eines verschleierten Zustands nie ausgeführt wird - stimme ich dafür, als Duplikat dieser Frage zu schließen .
Steve Summit

Antworten:

10

[Für die Aufzeichnung habe ich diese Antwort ziemlich erheblich bearbeitet, da sie akzeptiert und abgestimmt wurde. Es sagt aber immer noch im Grunde die gleichen Dinge.]

Dieser Code ist zutiefst, vielleicht absichtlich, verwirrend. Es enthält eine eng abgewendete Instanz des undefinierten Dread- Verhaltens . Es ist grundsätzlich unmöglich festzustellen, ob die Person, die diese Frage konstruiert hat, sehr, sehr klug oder sehr, sehr dumm war. Und die "Lektion", über die dieser Code Sie unterrichten oder befragen könnte - nämlich, dass der unäre Plus-Operator nicht viel tut - ist sicherlich nicht wichtig genug, um diese Art von subversiver Fehlleitung zu verdienen.

Es gibt zwei verwirrende Aspekte des Codes, die seltsame Bedingung:

while(+(+k--)!=0)

und die wahnsinnige Aussage, die es kontrolliert:

k=k++;

Ich werde zuerst den zweiten Teil behandeln.

Wenn Sie eine solche Variable haben k, die Sie um 1 erhöhen möchten, bietet C Ihnen nicht eine, nicht zwei, nicht drei, sondern vier verschiedene Möglichkeiten:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

Trotz dieser Prämie (oder vielleicht gerade deswegen) sind einige Programmierer verwirrt und husten Verrenkungen wie

k = k++;

Wenn Sie nicht herausfinden können, was dies tun soll, machen Sie sich keine Sorgen: Niemand kann. Dieser Ausdruck enthält zwei verschiedene Versuche, kden Wert zu ändern (das k =Teil und das k++Teil). Da es in C keine Regel gibt, die besagt, welche der versuchten Modifikationen "gewinnt", ist ein Ausdruck wie dieser formal undefiniert , was nicht nur das bedeutet es hat keine definierte Bedeutung, aber das gesamte Programm, das es enthält, ist verdächtig.

Nun, wenn man sich sehr sorgfältig, werden Sie , dass in diesem speziellen Programm sehen, die Linie k = k++nicht tatsächlich ausgeführt werden, weil (wie wir über zu sehen sind) die Kontrolle Zustand anfänglich falsch ist, so dass die Schleife 0 mal läuft . Also dieses besondere Programm möglicherweise nicht tatsächlich undefiniert sein - aber es ist immer noch pathologisch verwirrend.

Siehe auch diese kanonischen SO-Antworten auf alle Fragen zu undefiniertem Verhalten dieser Art.

Aber du hast nicht nach dem k=k++Teil gefragt . Sie haben nach dem ersten verwirrenden Teil gefragt, dem +(+k--)!=0Zustand. Das sieht seltsam, denn es ist seltsam. Niemand würde jemals einen solchen Code in ein echtes Programm schreiben. Es gibt also keinen Grund zu lernen, wie man es versteht. (Ja, es ist wahr, das Erforschen der Grenzen eines Systems kann Ihnen helfen, seine Feinheiten kennenzulernen, aber in meinem Buch gibt es eine ziemlich klare Grenze zwischen einfallsreichen, zum Nachdenken anregenden Erkundungen und düsteren, missbräuchlichen Erkundungen, und dieser Ausdruck ist sehr deutlich die falsche Seite dieser Linie.)

Wie auch immer, lassen Sie uns untersuchen +(+k--)!=0. (Und nachdem wir das getan haben, vergessen wir alles.) Jeder Ausdruck wie dieser muss von innen nach außen verstanden werden. Ich nehme an, du weißt was

k--

tut. Es nimmt kden aktuellen Wert und "gibt" ihn an den Rest des Ausdrucks zurück und dekrementiert mehr oder weniger gleichzeitig, dh kes speichert die Menge k-1zurück in k.

Aber was macht das +dann? Dies ist ein unäres Plus, kein binäres Plus. Es ist wie ein unäres Minus. Sie wissen, dass das binäre Minus subtrahiert: der Ausdruck

a - b

subtrahiert b von a. Und Sie wissen, dass unäres Minus die Dinge negiert: den Ausdruck

-a

gibt Ihnen das Negativ von a. Was Unary +tut, ist ... im Grunde nichts. +agibt Ihnen aden Wert, nachdem Sie positive Werte in positive und negative Werte in negative geändert haben. Also der Ausdruck

+k--

gibt dir, was k--dir gegeben hat, das ist kder alte Wert.

Aber wir sind noch nicht fertig, weil wir haben

+(+k--)

Dies nimmt nur das, was +k--Sie gegeben haben, und gilt wieder ungleichmäßig +. Also gibt es dir, was +k--dir gegeben hat, was k--dir gegeben hat, was kder alte Wert war.

Also am Ende die Bedingung

while(+(+k--)!=0)

macht genau das Gleiche wie der viel gewöhnlichere Zustand

while(k-- != 0)

hätte es getan. (Es macht auch das Gleiche wie die noch komplizierter aussehende Bedingung while(+(+(+(+k--)))!=0). Und diese Klammern sind nicht wirklich notwendig; es macht auch das Gleiche wie while(+ + + +k--!=0)es getan hätte.)

Sogar herauszufinden, was der "normale" Zustand ist

while(k-- != 0)

tut ist irgendwie schwierig. In dieser Schleife gibt es zwei Dinge: Da die Schleife möglicherweise mehrmals ausgeführt wird, werden wir:

  1. mach weiter k--, um immer kkleiner zu machen , aber auch
  2. Mach weiter mit dem Körper der Schleife, was auch immer das tut.

Aber wir erledigen den k--Teil sofort, bevor wir (oder gerade) entscheiden, ob wir eine weitere Reise durch die Schleife unternehmen wollen. Und denken Sie daran, dass k--der alte Wert von "zurückgegeben" wird k, bevor er dekrementiert wird. In diesem Programm ist der Anfangswert von k0. k--Der alte Wert 0 wird also "zurückgegeben" und dann kauf -1 aktualisiert . Aber dann ist der Rest der Bedingung != 0- aber wie wir gerade gesehen haben, haben wir beim ersten Testen der Bedingung eine 0 erhalten. Wir werden also keine Fahrten durch die Schleife machen, also werden wir nicht versuchen, die auszuführen überhaupt problematische Aussage k=k++.

Mit anderen Worten, in dieser speziellen Schleife stellt sich heraus, dass Sache 1 einmal passiert, Sache 2 jedoch null Mal, obwohl ich gesagt habe, dass "zwei Dinge vor sich gehen".

Auf jeden Fall hoffe ich, dass jetzt hinreichend klar ist, warum diese schlechte Entschuldigung für ein Programm am Ende -1 als Endwert von druckt k. Normalerweise beantworte ich Quizfragen wie diese nicht gerne - es fühlt sich an wie Betrug -, aber in diesem Fall macht es mir nichts aus, da ich mit dem ganzen Punkt der Übung so lautstark nicht einverstanden bin.

Steve Summit
quelle
1
Erfundene Beispiele sind typisch für jede Grundklasse. Wie sonst würde man eine kurze Frage zum Vorrang des Bedieners bei einer Prüfung schreiben ? Ich unterrichte Mathematik, und wenn ich jedes Mal einen Nickel hätte, wenn ein Schüler fragte: "Wann mache ich das im wirklichen Leben?" ...
Scott
4
@Scott Es gibt erfunden, und dann gibt es erfunden . Angenommen, Sie sind Fahrlehrer. Angenommen, Sie bitten Ihren Schüler, durch starken Verkehr in der Innenstadt zu fahren, während Sie ihn mit einem Baseballschläger um den Kopf schlagen. Der Schüler wird wahrscheinlich eine potenziell nützliche Fähigkeit erlernen. Aber ich nenne das Missbrauch. Und ich stehe zu meiner Meinung, dass der Code in dieser Frage missbräuchlich ist und einen negativen pädagogischen Wert hat.
Steve Summit
1
"Ich stehe zu meiner Meinung" das ist fair, aber das Schlüsselwort hier ist "Meinung". Eine StackOverflow-Antwort ist möglicherweise nicht der optimale Ort, um seine Meinung zu äußern. :)
Scott
1
Für die Aufzeichnung habe ich diese Antwort ziemlich erheblich bearbeitet, da sie akzeptiert und abgestimmt wurde. Es sagt aber immer noch im Grunde die gleichen Dinge.
Steve Summit
11

Auf den ersten Blick sieht es so aus, als würde dieser Code undefiniertes Verhalten hervorrufen, was jedoch nicht der Fall ist.

Lassen Sie uns zuerst den Code richtig formatieren:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

Jetzt können wir also sehen, dass sich die Anweisung k=k++;innerhalb der Schleife befindet.

Verfolgen wir nun das Programm:

Wenn die Schleifenbedingung zum ersten Mal ausgewertet wird, khat sie den Wert 0. Der Ausdruck k--hat den aktuellen Wert von k0 und kwird als Nebeneffekt dekrementiert. Nach dieser Anweisung ist der Wert von k-1.

Der führende +Wert für diesen Ausdruck hat keine Auswirkung auf den Wert, wird also +k--mit 0 und in ähnlicher Weise +(+k--)mit 0 bewertet.

Dann wird der !=Bediener ausgewertet. Da 0!=0false ist, wird der Hauptteil der Schleife nicht eingegeben . Wäre der Body eingegeben worden, würden Sie undefiniertes Verhalten aufrufen, da k=k++sowohl gelesen als auch geschrieben wird, kohne einen Sequenzpunkt. Aber die Schleife ist nicht eingegeben, also keine UB.

Schließlich wird der Wert von k-1 gedruckt.

dbush
quelle
1
Es ist eine offene, existenzielle, philosophische, unabwägbare Frage, ob undefiniertes Verhalten, das nicht ausgeführt wird, nicht undefiniert ist. Ist es nicht undefiniert? Das glaube ich nicht.
Steve Summit
2
@SteveSummit Es gibt absolut nichts Existenzielles, Philosophisches, Unwägbares. if (x != NULL) *x = 42;Ist das undefiniert wann x == NULL? Natürlich nicht. Undefiniertes Verhalten tritt nicht in Teilen des Codes auf, die nicht ausgeführt werden. Das Wort Verhalten ist ein Hinweis. Code, der nicht ausgeführt wird, hat kein undefiniertes oder sonstiges Verhalten.
n. 'Pronomen' m.
1
@SteveSummit Wenn undefiniertes Verhalten, das nicht ausgeführt wird, das gesamte Programm ungültig machen würde, hätte kein C-Programm Verhalten definiert. Weil C-Programme immer nur eine Schleife / if-Bedingung von der Ausführung von UB entfernt sind. Nehmen Sie als Beispiel eine einfache Array-Iteration: Sie iteriert, bis die gebundene Prüfung fehlschlägt. Das Ausführen der nächsten Schleifeniteration wäre ein undefiniertes Verhalten, aber da dies nie ausgeführt wird, ist alles in Ordnung.
cmaster - wieder Monica
3
Ich werde nicht darüber streiten, weil ich keinen Sprachanwalt mache. Aber ich wette, wenn Sie diese Frage stellen und sie als Sprachanwalt markieren, könnten Sie eine lebhafte Debatte in Gang bringen. Beachten Sie, dass dies k=k++qualitativ anders ist als *x=42. Letzteres ist genau definiert, wenn xes sich um einen gültigen Zeiger handelt, Ersteres ist jedoch undefiniert, egal was passiert. (Ich gebe zu, dass Sie vielleicht Recht haben, aber ich werde nicht darüber streiten, und ich mache mir zunehmend Sorgen, dass wir meisterhaft getrollt wurden.)
Steve Summit
1
"Letzteres ist gut definiert, wenn x ein gültiger Zeiger ist, aber Ersteres ist undefiniert, egal was passiert." Nur ganze Programme dürfen undefiniertes Verhalten haben. Dieser Begriff gilt nicht für Codefragmente, die isoliert betrachtet werden.
n. 'Pronomen' m.
3

Hier ist eine Version davon, die die Priorität des Operators zeigt:

+(+(k--))

Die beiden unären +Operatoren tun nichts, daher entspricht dieser Ausdruck genau k--. Die Person, die dies geschrieben hat, hat höchstwahrscheinlich versucht, sich mit Ihrem Verstand herumzuschlagen.

SS Anne
quelle