Warum zeigt diese Version von logischem UND in C kein Kurzschlussverhalten?

Lesezeit: 12 Minuten

Benutzeravatar von Bobazonski
Bobazonski

Ja, das ist eine Hausaufgabenfrage, aber ich habe meine Nachforschungen angestellt und ziemlich viel über das Thema nachgedacht und kann das nicht herausfinden. Die Frage besagt, dass dieses Stück Code NICHT ausgestellt wird Kurzschlussverhalten und fragt warum. Aber es sieht für mich so aus, als würde es ein Kurzschlussverhalten aufweisen. Kann jemand erklären, warum dies nicht der Fall ist?

In C:

int sc_and(int a, int b) {
    return a ? b : 0;
}

So sieht es für mich in dem Fall aus a falsch ist, versucht das Programm nicht, es auszuwerten b überhaupt, aber ich muss mich irren. Warum berührt das Programm überhaupt b in diesem Fall, wenn es nicht sein muss?

  • Wie es für die meisten erfundenen Hausaufgaben typisch ist, werden Sie diese Art von Code niemals in einem Produktionssystem sehen, es sei denn, der Programmierer versucht absichtlich, clever zu sein.

    – Robert Harvey

    13. Oktober 2014 um 16:00 Uhr

  • @RobertHarvey Sie werden in Produktionssystemen ständig genau solchen Code sehen! Es ist unwahrscheinlich, dass es sich um eine benannte Funktion handelt AND(), aber Funktionen, die Argumente nach Wert empfangen und sie dann je nach Logik der Funktion auswerten (oder nicht), sind überall. Obwohl es sich um eine “Trickfrage” handelt, ist es ein kritisches Verhalten von C zu verstehen. In einer Sprache mit Call-by-Name würde diese Frage ganz anders beantwortet werden.

    – Ben Jackson

    13. Oktober 2014 um 16:50 Uhr

  • @BenJackson: Ich habe den Code kommentiert, nicht das Verhalten. Ja, Sie müssen das Verhalten verstehen. Nein, Sie müssen keinen Code wie diesen schreiben.

    – Robert Harvey

    13. Oktober 2014 um 17:18 Uhr


  • Dies ist eigentlich ziemlich relevant, wenn Sie jemals in VB codieren müssen und auf IIf stoßen. Da es sich eher um eine Funktion als um einen Operator handelt, wird die Auswertung nicht kurzgeschlossen. Dies kann für Entwickler, die an den kurzschließenden Operator gewöhnt sind, zu Problemen führen, die dann Dinge wie schreiben IIf(x Is Nothing, Default, x.SomeValue).

    – Dan Bryant

    13. Oktober 2014 um 19:28 Uhr


  • @alk weil es so ist a Anklage gegen das Bildungssystem.

    – Shivan-Drache

    14. Oktober 2014 um 10:49 Uhr

Benutzeravatar von aruisdante
aruisdante

Dies ist eine Fangfrage. b ist ein Eingabeargument für die sc_and Methode und wird daher immer evaluiert. Mit anderen Worten sc_and(a(), b()) werde anrufen a() und Ruf an b() (Bestellung nicht garantiert), dann anrufen sc_and mit den Ergebnissen von a(), b() der übergeht a?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 eine „Trickfrage“ genannt habe: Es liegt an dem Mangel an klar definiertem Kontext, wo „Kurzschluss“ zu berücksichtigen ist (zumindest wie vom OP reproduziert). Viele Personen, wenn ihnen nur eine Funktionsdefinition gegeben wird, Gehen Sie davon aus, dass der Kontext der Frage die Frage nach dem ist Karosserie der Funktion; Sie betrachten die Funktion oft nicht als Ausdruck an sich. Das ist der „Trick“ der Frage; Um Sie daran zu erinnern, dass Sie das beim Programmieren im Allgemeinen, aber insbesondere in Sprachen wie C-likes, die oft viele Ausnahmen von Regeln haben, nicht tun können. Beispiel, wenn die Frage so gestellt wurde:

Betrachten Sie den folgenden Code. Wird sc_and Kurzschlussverhalten zeigen, wenn es von aufgerufen wird hauptsächlich:

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 denken sollen sc_and als eigenständiger Operator in Ihrem eigenen domänenspezifische Spracheund bewerten, ob die Aufruf sc_and zeigt Kurzschlussverhalten wie ein normaler && möchten. Ich würde das überhaupt nicht als Fangfrage betrachten, denn es ist klar, dass Sie sich nicht auf den ternären Operator konzentrieren sollen, sondern auf die Funktionsaufrufmechanik von C/C++ (und ich würde vermuten, führen schön in eine Folgefrage zu schreiben sc_and das macht einen Kurzschluss, was die Verwendung von a beinhalten würde #define eher als eine Funktion).

Ob Sie das, was der ternäre Operator selbst tut, Kurzschließen nennen oder nicht, hängt von Ihrer Definition des Kurzschließens ab, und Sie können die verschiedenen Kommentare lesen, um sich Gedanken darüber zu machen. Bei mir schon, aber es ist nicht besonders relevant für die eigentliche Frage oder warum ich es einen “Trick” genannt habe.

  • Der ternäre Operator macht Kurzschluss? Nein. Es wertet einen der Ausdrücke nach dem aus ?genau wie in if (condition) {when true} else {when-false}. Das nennt man nicht Kurzschluss.

    – Jens

    13. Oktober 2014 um 15:54 Uhr


  • @Jens: Ich würde jeden Fall, in dem ein Operator die Auswertung eines oder mehrerer seiner Operanden überspringt, als eine Form von “Kurzschluss” bezeichnen, aber das ist wirklich nur eine Frage der Definition des Begriffs.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    13. Oktober 2014 um 17:34 Uhr

  • @Jens Es ist syntaktischer Zucker für einen if elsenicht ein if. Und selbst dann ist es nicht wirklich; das ?: Betreiber ist ein Ausdruck, if-else ist ein Aussage. Das sind semantisch sehr unterschiedliche Dinge, auch wenn Sie eine äquivalente Menge konstruieren können Aussagen die den gleichen Effekt wie die ternäre hervorrufen Ausdruck. Aus diesem Grund wird es als Kurzschluss betrachtet; die meisten anderen Ausdrücke werten immer alle ihre Operanden aus (+,-,*,/ etc). Wenn sie es nicht tun, ist es ein Kurzschluss (&&, ||).

    – aruisdante

    13. Oktober 2014 um 19:47 Uhr


  • Ja, für mich ist der wichtige Unterschied, dass wir über Operatoren sprechen. Na sicher Anweisungen zur Flusskontrolle steuern, welcher Codepfad ausgeführt wird. Für Operatoren ist es für jemanden, der nicht mit C und C-abgeleiteten Sprachen vertraut ist, nicht offensichtlich, dass ein Operator möglicherweise nicht alle seine Operanden auswertet, daher ist es wichtig, einen Begriff zu haben, um über diese Eigenschaft zu sprechen, und dafür verwende ich “short Schaltung”.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    13. Oktober 2014 um 20:16 Uhr

Benutzeravatar von hackks
hackt

Bei der Aussage

bool x = a && b++;  // a and b are of int type

führt aus, b++ wird nicht ausgewertet, wenn der Operand a ausgewertet zu false (Kurzschlussverhalten). Dies bedeutet, dass die Nebenwirkung auf b findet nicht statt.

Betrachten Sie nun die Funktion:

bool and_fun(int a, int b)
{
     return a && b; 
}

und nenne das

bool x = and_fun(a, b++);

Ob in diesem Fall a ist true oder false, b++ wird immer ausgewertet1 während Funktionsaufruf und Seiteneffekt an b wird immer stattfinden.

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 Auswertung von Funktionsargumenten ist nicht festgelegt.

  • OK, also in Übereinstimmung mit der Antwort von Stephen Quan: Ist es möglich (legal), dass ein Compilar die Funktion “and_fun” einbetten könnte, sodass beim Aufruf von “bool x = and_fun(a, b++);” b++ wird nicht erhöht, wenn a wahr ist ?

    – Shivan-Drache

    14. Oktober 2014 um 10:45 Uhr

  • @ShivanDragon Das klingt für mich nach einer Änderung des beobachtbaren Verhaltens.

    – Sapi

    14. Oktober 2014 um 11:54 Uhr

  • @ShivanDragon: Beim Inlining ändert der Compiler das Verhalten nicht. Es ist nicht so einfach, den Funktionskörper in zu ersetzen.

    – Jack Aidley

    14. Oktober 2014 um 12:25 Uhr

  • Zur Verdeutlichung könnten Sie hinzufügen int x = a ? b++ : 0als beobachtbarer Kurzschluss.

    – Paul Tucher

    14. Oktober 2014 um 18:27 Uhr

  • @PaulDraper; Ich habe das Original-Snippet beibehalten, damit die Leser es nicht verwirren.

    – Hacken

    14. Oktober 2014 um 20:03 Uhr

Benutzeravatar von alk
alk

Wie bereits von anderen betont, wird, egal was als die beiden Argumente an die Funktion übergeben wird, es ausgewertet, wenn es übergeben wird. Das ist lange vor der Tenary-Operation.

Andererseits diese

#define sc_and(a, b) \
  ((a) ?(b) :0)

möchten “Kurzschluss”, wie dies Makro impliziert keinen Funktionsaufruf und damit wird keine Auswertung der Argumente einer Funktion durchgeführt.

  • vielleicht erklären warum? Es sieht für mich genauso aus wie das Snippet der OP. Nur dass sein Inline anstelle eines anderen Bereichs ist.

    – dhein

    14. Oktober 2014 um 9:50 Uhr

Benutzeravatar der Miniaturansicht
Miniaturansicht

Bearbeitet, um die im Kommentar von @cmasters festgestellten Fehler zu korrigieren.


Im

int sc_and(int a, int b) {
    return a ? b : 0;
}

… das returnDer ed-Ausdruck weist eine Kurzschlussauswertung auf, der Funktionsaufruf jedoch nicht.

Versuchen Sie anzurufen

sc_and (0, 1 / 0);

Der Funktionsaufruf wertet aus 1 / 0obwohl es nie verwendet wird, wodurch – wahrscheinlich – ein Fehler bei der Division durch Null verursacht wird.

Relevante Auszüge aus dem (Entwurf) ANSI C Standard sind:

2.1.2.3 Programmausführung

In der abstrakten Maschine werden alle Ausdrücke so ausgewertet, wie es die Semantik vorgibt. Eine tatsächliche Implementierung muss einen Teil eines Ausdrucks nicht auswerten, wenn daraus abgeleitet werden kann, dass sein Wert nicht verwendet wird und dass keine erforderlichen Nebeneffekte erzeugt werden (einschließlich solcher, die durch das Aufrufen einer Funktion oder den Zugriff auf ein flüchtiges Objekt verursacht werden).

und

3.3.2.2 Funktionsaufrufe

….

Semantik

Bei der Vorbereitung des Aufrufs einer Funktion werden die Argumente ausgewertet und jedem Parameter der Wert des entsprechenden Arguments zugewiesen.

Meine Vermutung ist, dass jedes Argument als Ausdruck ausgewertet wird, die Argumentliste jedoch als Ganzes nicht ein Ausdruck, daher ist das Nicht-SCE-Verhalten obligatorisch.

Als Splasher an der Oberfläche der tiefen Gewässer des C-Standards würde ich eine richtig informierte Sicht auf zwei Aspekte schätzen:

  • Bewertet 1 / 0 undefiniertes Verhalten erzeugen?
  • Ist eine Argumentliste ein Ausdruck? (Ich denke nicht)

PS

Sogar Sie wechseln zu C++ und definieren sc_and als ein inline funktionieren, werden Sie nicht SCE bekommen. Wenn Sie es als C-Makro definieren, wie es @alk tut, werden Sie es sicherlich tun.

Um den Kurzschluss von ternären Operationen deutlich zu sehen, versuchen Sie, den Code leicht zu ändern, 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 (sogar fast ohne Optimierung: -O0!). Du wirst sehen b() wird nicht ausgeführt, wenn a() gibt falsch zurück.

% gcc -O0 tershort.c            
% ./a.out 
I'm a() returning 0
% 

Hier sieht die generierte Assembly so 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 darauf hingewiesen haben, besteht der Fragentrick darin, dass Sie sich auf das Verhalten des ternären Operators konzentrieren, das einen Kurzschluss auslöst (nennen Sie das als „bedingte Bewertung“), anstatt auf die Parameterauswertung bei Anruf (Anruf nach Wert), die NICHT kurzschließt.

Der ternäre C-Operator kann niemals kurzschließen, da er nur einen einzigen Ausdruck auswertet a (die Bedingung), um einen durch Ausdrücke gegebenen Wert zu bestimmen b und cwenn 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 durch andere Operatoren wie && oder || gebildet werden die kurzschließen können, weil sie möglicherweise zwei Ausdrücke auswerten, bevor sie einen Wert zurückgeben, aber das würde nicht als der ternäre Operator betrachtet werden, der einen Kurzschluss ausführt, sondern die Operatoren, die in der Bedingung verwendet werden, wie es in einer regulären if-Anweisung der Fall ist.

Aktualisieren:

Es gibt einige Diskussionen darüber, dass der ternäre Operator ein Kurzschlussoperator ist. Das Argument besagt, dass jeder Operator, der nicht alle seine Operanden auswertet, gemäß @aruisdante im Kommentar unten einen Kurzschluss verursacht. Wenn diese Definition gegeben wäre, würde der ternäre Operator kurzschließen, und falls dies die ursprüngliche Definition ist, stimme ich zu. Das Problem ist, dass der Begriff “Kurzschluss” ursprünglich für eine bestimmte Art von Operatoren verwendet wurde, die dieses Verhalten zuließen, und das sind die logischen/booleschen Operatoren, und der Grund, warum nur diese sind, werde ich versuchen zu erklären.

Nach dem Artikel Kurzschlussauswertungbezieht sich die Kurzschlussauswertung nur auf boolesche Operatoren, die so in die Sprache implementiert sind, dass das Wissen, dass der erste Operand den zweiten irrelevant macht, da der &&-Operator der erste Operand ist FALSCH, und für die || Operator ist der erste Operand Stimmt, die C11-Spezifikation vermerkt es auch in 6.5.13 Logischer AND-Operator und 6.5.14 Logischer OR-Operator.

Das bedeutet, dass Sie für das zu identifizierende Kurzschlussverhalten erwarten würden, es in einem Operator zu identifizieren, 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 geschrieben steht MathWorks unter dem Abschnitt “Logisches Kurzschließen”, da das Kurzschließen von den logischen Operatoren herrührt.

Wie ich versucht habe zu erklären, wertet der ternäre Operator von C, auch ternäres if genannt, nur zwei der Operanden aus, er wertet den ersten aus und dann einen zweiten, wobei je nach Wert von einer der beiden übrig bleibt Erster. Das tut es immer, es soll in keiner Situation alle drei auswerten, 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 mit einer Ablehnung, das macht die SO-Erfahrung nur noch schlimmer, und ich glaube, wir können eine viel bessere Community sein als eine, die nur Antworten ablehnt man ist damit nicht einverstanden.

1418290cookie-checkWarum zeigt diese Version von logischem UND in C kein Kurzschlussverhalten?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy