Ja, dies ist eine Hausaufgabenfrage, aber ich habe meine Nachforschungen angestellt und ziemlich viel über das Thema nachgedacht und kann dies nicht herausfinden. Die Frage besagt, dass dieser Code KEIN Kurzschlussverhalten aufweist und fragt, warum. Aber es sieht für mich so aus, als ob es ein Kurzschlussverhalten zeigt. Kann jemand erklären, warum dies nicht der Fall ist?
In C:
int sc_and(int a, int b) {
return a ? b : 0;
}
Es sieht für mich so aus a
, als würde das Programm im falschen Fall überhaupt nicht versuchen, eine Bewertung b
vorzunehmen, aber ich muss mich irren. Warum berührt das Programm b
in diesem Fall überhaupt, wenn es nicht muss?
c
short-circuiting
Bobazonski
quelle
quelle
AND()
, aber Funktionen, die Argumente nach Wert empfangen und sie dann auswerten (oder nicht), abhängig von der Logik der Funktion, sind überall. Obwohl es sich um eine "Trickfrage" handelt, ist es ein kritisches Verhalten von C, dies zu verstehen. In einer Sprache mit Call-by-Name hätte diese Frage eine ganz andere Antwort.IIf(x Is Nothing, Default, x.SomeValue)
.Antworten:
Dies ist eine Trickfrage.
b
ist ein Eingabeargument für diesc_and
Methode und wird daher immer ausgewertet. Mit anderen Worten, essc_and(a(), b())
wird angerufena()
und angerufenb()
(Reihenfolge nicht garantiert), und dann angerufen,sc_and
dessen Ergebnissea(), b()
an weitergeleitet werdena?b:0
. Es hat nichts mit dem ternären Operator selbst zu tun, der absolut kurzschließen würde.AKTUALISIEREN
In Bezug darauf, warum ich dies als "Trickfrage" bezeichnet habe: Es liegt an dem Mangel an genau definierten Zusammenhängen, in denen "Kurzschluss" (zumindest wie vom OP reproduziert) zu berücksichtigen ist. Viele Personen gehen, wenn sie nur eine Funktionsdefinition erhalten, davon aus, dass der Kontext der Frage nach dem Körper der Funktion fragt ; Sie betrachten die Funktion oft nicht als Ausdruck an und für sich. Dies ist der "Trick" der Frage; Um Sie daran zu erinnern, dass Sie beim Programmieren im Allgemeinen, aber insbesondere in Sprachen wie C-Likes, die oft viele Ausnahmen von Regeln haben, dies nicht tun können. Beispiel, wenn die Frage als solche gestellt wurde:
int sc_and(int a, int b){ return a?b:0; } int a(){ cout<<"called a!"<<endl; return 0; } int b(){ cout<<"called b!"<<endl; return 1; } int main(char* argc, char** argv){ int x = sc_and(a(), b()); return 0; }
Es wäre sofort klar, dass Sie
sc_and
als Operator an und für sich in Ihrer eigenen domänenspezifischen Sprache denken und bewerten sollten, ob der Aufruf, einsc_and
Kurzschlussverhalten wie ein normaler&&
zu zeigen . Ich würde das überhaupt nicht als Trickfrage betrachten, da klar ist, dass Sie sich nicht auf den ternären Operator konzentrieren sollten, sondern sich stattdessen auf die Funktionsaufrufmechanik von C / C ++ konzentrieren sollten (und ich würde vermuten, führen schön in eine Folgefrage zu schreibensc_and
, die einen Kurzschluss macht, der die Verwendung einer#define
statt einer Funktion beinhalten würde).Ob Sie das, was der ternäre Operator selbst kurzschließt (oder etwas anderes, wie "bedingte Bewertung"), aufrufen oder nicht, hängt von Ihrer Definition des Kurzschlusses ab, und Sie können die verschiedenen Kommentare lesen, um darüber nachzudenken. Meins schon, aber es ist nicht besonders relevant für die eigentliche Frage oder warum ich es einen "Trick" genannt habe.
quelle
?
, genau wie inif (condition) {when true} else {when-false}
. Das nennt man nicht Kurzschluss.x Sand y
(unter Verwendung von Sand zur Bezeichnung der Kurzschlussvielfalt) entspricht dem bedingten Ausdruck, demif x then y else false;
der Ausdruckx Sor y
entsprichtif x then true else y
. .if else
, nicht für einenif
. Und selbst dann ist es nicht wirklich; Der?:
Operator ist ein Ausdruck ,if-else
eine Anweisung . Das sind semantisch sehr unterschiedliche Dinge, selbst wenn Sie eine äquivalente Menge von Anweisungen erstellen können, die den gleichen Effekt wie der ternäre Ausdruck erzeugen . Aus diesem Grund wird es als Kurzschluss angesehen. Die meisten anderen Ausdrücke bewerten immer alle ihre Operanden (+,-,*,/
usw.). Wenn nicht, ist es ein Kurzschluss (&&, ||
).Wenn die Aussage
bool x = a && b++; // a and b are of int type
wird ausgeführt,
b++
wird nicht ausgewertet, wenn der Operanda
ausgewertet wirdfalse
(Kurzschlussverhalten). Dies bedeutet, dass die Nebenwirkungb
nicht auftritt.Schauen Sie sich nun die Funktion an:
bool and_fun(int a, int b) { return a && b; }
und nenne das
bool x = and_fun(a, b++);
In diesem Fall , ob
a
isttrue
oderfalse
,b++
immer ausgewertet wird 1 bei Funktionsaufruf und Nebenwirkung aufb
Platz wird immer nehmen.Gleiches gilt für
int x = a ? b : 0; // Short circuit behavior
und
int sc_and (int a, int b) // No short circuit behavior. { return a ? b : 0; }
1 Die Reihenfolge der Bewertung von Funktionsargumenten ist nicht angegeben.
quelle
int x = a ? b++ : 0
als beobachtbaren Kurzschluss hinzufügen .Wie bereits von anderen erwähnt, wird unabhängig davon, was als die beiden Argumente in die Funktion übergeben wird, diese ausgewertet, sobald sie übergeben wird. Das ist weit vor der Tenary-Operation.
Auf der anderen Seite dies
#define sc_and(a, b) \ ((a) ?(b) :0)
würde "kurzschließen", da dieses Makro keinen Funktionsaufruf impliziert und damit keine Bewertung der Argumente einer Funktion durchgeführt wird.
quelle
Bearbeitet, um die im Kommentar von @cmasters angegebenen Fehler zu korrigieren.
Im
int sc_and(int a, int b) { return a ? b : 0; }
... der
return
Ausdruck ed zeigt eine Kurzschlussauswertung, der Funktionsaufruf jedoch nicht.Versuchen Sie anzurufen
sc_and (0, 1 / 0);
Der Funktionsaufruf wird ausgewertet
1 / 0
, obwohl er nie verwendet wird, was wahrscheinlich zu einem Fehler beim Teilen durch Null führt.Relevante Auszüge aus dem (Entwurf) des ANSI C-Standards sind:
und
Ich vermute, dass jedes Argument als Ausdruck ausgewertet wird, die gesamte Argumentliste jedoch kein Ausdruck ist, weshalb das Nicht-SCE-Verhalten obligatorisch ist.
Als Spritzer auf der Oberfläche der tiefen Gewässer des C-Standards würde ich eine gut informierte Sicht auf zwei Aspekte begrüßen:
1 / 0
undefiniertes Verhalten?PS
Selbst wenn Sie zu C ++ wechseln und
sc_and
alsinline
Funktion definieren , erhalten Sie keine SCE. Wenn Sie es wie @alk als C-Makro definieren , werden Sie es sicherlich tun .quelle
inline
Funktion ändert die Semantik eines Funktionsaufrufs nicht. Und geben Sie diese Semantik , dass alle Argumente werden ausgewertet, wie Sie richtig zitiert. Selbst wenn der Compiler den Aufruf optimieren kann,sc_and(f(), g())
darf sich das sichtbare Verhalten nicht ändern, dh es muss sich so verhalten, als ob beidef()
undg()
immer aufgerufen werden.sc_and(0, 1/0)
ist ein schlechtes Beispiel , weil es ist nicht definiertes Verhalten, und der Compiler ist nicht erforderlich , um auch Callsc_and()
...inline
die Aufrufsemantik beibehält. Ich habe mich an die Nullteilung gehalten, wie es zum Beispiel passt, und SCE wird meiner Meinung nach oft ausgenutzt, um undefiniertes Verhalten zu vermeiden .Um ternäre Kurzschlüsse deutlich zu erkennen, ändern Sie den Code geringfügig, um Funktionszeiger anstelle von Ganzzahlen zu verwenden:
int a() { printf("I'm a() returning 0\n"); return 0; } int b() { printf("And I'm b() returning 1 (not that it matters)\n"); return 1; } int sc_and(int (*a)(), int (*b)()) { a() ? b() : 0; } int main() { sc_and(a, b); return 0; }
Und dann kompilieren Sie es (auch mit fast KEINER Optimierung :
-O0
!). Sie werden sehen, dassb()
es nicht ausgeführt wird, wenna()
false zurückgegeben wird.% gcc -O0 tershort.c % ./a.out I'm a() returning 0 %
Hier sieht die generierte Baugruppe folgendermaßen aus:
call *%rdx <-- call a() testl %eax, %eax <-- test result je .L8 <-- skip if 0 (false) movq -16(%rbp), %rdx movl $0, %eax call *%rdx <- calls b() only if not skipped .L8:
Wie andere richtig betonten, besteht der Fragetrick darin, dass Sie sich auf das ternäre Operatorverhalten konzentrieren, das einen Kurzschluss verursacht (nennen Sie dies 'bedingte Auswertung'), anstatt auf die Parameterauswertung beim Aufruf (Aufruf nach Wert), die NICHT kurzschließt.
quelle
Der ternäre Operator C kann niemals einen Kurzschluss verursachen, da er nur einen einzelnen Ausdruck a (die Bedingung) auswertet , um einen durch die Ausdrücke b und c gegebenen Wert zu bestimmen , falls ein Wert zurückgegeben werden könnte.
Der folgende Code:
int ret = a ? b : c; // Here, b and c are expressions that return a value.
Es entspricht fast dem folgenden Code:
int ret; if(a) {ret = b} else {ret = c}
Der Ausdruck a kann von anderen Operatoren wie && oder || gebildet werden Dies kann einen Kurzschluss verursachen, da sie möglicherweise zwei Ausdrücke auswerten, bevor sie einen Wert zurückgeben. Dies wird jedoch nicht als ternärer Kurzschlussoperator angesehen, sondern als Operator, der in der Bedingung wie in einer regulären if-Anweisung verwendet wird.
Aktualisieren:
Es gibt einige Debatten darüber, dass der ternäre Operator ein Kurzschlussoperator ist. Das Argument besagt, dass jeder Operator, der nicht alle Operanden auswertet, gemäß @aruisdante im Kommentar unten einen Kurzschluss macht. Wenn diese Definition gegeben wäre, würde der ternäre Operator kurzschließen, und in dem Fall, dass dies die ursprüngliche Definition ist, stimme ich zu. Das Problem ist, dass der Begriff "Kurzschluss" ursprünglich für eine bestimmte Art von Operator verwendet wurde, der dieses Verhalten zuließ, und dies sind die logischen / booleschen Operatoren, und der Grund, warum nur diese Operatoren vorhanden sind, werde ich zu erklären versuchen.
Nach dem Artikel Kurzschlussbewertung bezieht sich die Kurzschlussbewertung nur auf boolesche Operatoren, die in der Sprache so implementiert sind, dass das Wissen, dass der erste Operand den zweiten irrelevant macht, dies bedeutet, dass der Operator && der erste Operand falsch ist und für die || Da der Operator der erste wahre Operand ist , wird er in der C11-Spezifikation auch in 6.5.13 Logischer UND-Operator und 6.5.14 Logischer ODER-Operator angegeben.
Dies bedeutet, dass Sie das zu identifizierende Kurzschlussverhalten in einem Operator identifizieren müssen, der alle Operanden genau wie die booleschen Operatoren auswerten muss, wenn der erste Operand den zweiten nicht irrelevant macht. Dies steht im Einklang mit dem, was in einer anderen Definition für den Kurzschluss in MathWorks im Abschnitt "Logischer Kurzschluss" geschrieben ist, da der Kurzschluss von den logischen Operatoren stammt.
Da ich versucht habe, den C-ternären Operator zu erklären, der auch als ternär bezeichnet wird, wenn er nur zwei der Operanden auswertet, wertet er den ersten aus und wertet dann einen zweiten aus, wobei einer der beiden verbleibenden abhängig vom Wert des Erster. Es tut dies immer, es soll in keiner Situation alle drei bewerten, also gibt es auf keinen Fall einen "Kurzschluss".
Wie immer, wenn Sie sehen, dass etwas nicht stimmt, schreiben Sie bitte einen Kommentar mit einem Argument dagegen und nicht nur einer Abwertung, die die SO-Erfahrung nur verschlimmert, und ich glaube, wir können eine viel bessere Community sein als eine, die nur die Antworten ablehnt man stimmt nicht zu.
quelle
x = a ? b : 0;
ist semantisch dasselbe wie(a && (x = b)) || (x = 0);