Soll die Verwendung von if (0) zum Überspringen eines Falls in einem Schalter funktionieren?

Lesezeit: 6 Minuten

Benutzeravatar von Mark Adler
Markus Adler

Ich habe eine Situation, in der ich möchte, dass zwei Fälle in einer C++-Switch-Anweisung beide auf einen dritten Fall durchfallen. Insbesondere würde der zweite Fall in den dritten Fall durchfallen, und der erste Fall würde auch in den dritten Fall durchfallen ohne den zweiten Fall durchlaufen.

Ich hatte eine dumme Idee, probierte es aus und es funktionierte! Ich wickelte den zweiten Fall in ein if (0) {}. Es sieht aus wie das:

#ifdef __cplusplus
#  include <cstdio>
#else
#  include <stdio.h>
#endif

int main(void) {
    for (int i = 0; i < 3; i++) {
        printf("%d: ", i);
        switch (i) {
        case 0:
            putchar('a');
            // @fallthrough@
            if (0) {        // fall past all of case 1 (!)
        case 1:
            putchar('b');
            // @fallthrough@
            }
        case 2:
            putchar('c');
            break;
        }
        putchar('\n');
    }
    return 0;
}

Wenn ich es ausführe, bekomme ich die gewünschte Ausgabe:

0: ac
1: bc
2: c

Ich habe es sowohl in C als auch in C++ (beide mit Clang) ausprobiert, und es hat dasselbe getan.

Meine Fragen sind: Ist das gültiges C/C++? Soll es tun, was es tut?

  • Ja, das ist gültig und funktioniert aus ziemlich denselben Gründen wie Duffs Gerät.

    – dxiv

    22. Oktober 2020 um 6:00 Uhr


  • Beachten Sie, dass Code wie dieser Sie in einer Umgebung, die sich auch nur ein wenig um Lesbarkeit und Wartbarkeit kümmert, aus jeder Code-Komplettlösung herauswirft.

    – Andreas Henle

    22. Oktober 2020 um 13:09 Uhr

  • Das ist schrecklich. Noch schrecklicher als Duffs Gerät, wohlgemerkt. Verwandte, ich habe auch vor kurzem so etwas gesehen switch(x) { case A: case B: do_this(); if(x == B) also_do_that(); ... }. Das war auch, IMO, schrecklich. Bitte schreiben Sie solche Sachen einfach als ob-Anweisungen, auch wenn das bedeutet, dass Sie eine Zeile an zwei Stellen wiederholen müssen. Verwenden Sie Funktionen und Variablen (und Dokumentation!), um das Risiko einer versehentlichen späteren Aktualisierung an nur einer Stelle zu verringern.

    – Ilkkachu

    22. Oktober 2020 um 16:47 Uhr

  • 🙂 Für diejenigen, die verletzt oder verstümmelt wurden, weil sie sich diesen Code angesehen haben, ich habe nicht gesagt, dass es a war gut Idee. Eigentlich sagte ich, es sei eine dumme Idee.

    – Markus Adler

    22. Oktober 2020 um 19:28 Uhr

  • Beachten Sie, dass Konstruktionen in solchen Schaltern NICHT gut mit RAII funktionieren 🙁

    – Muhende Ente

    23. Oktober 2020 um 21:41 Uhr

Ja, das soll funktionieren. Die case-Labels für eine switch-Anweisung in C sind fast genau wie goto-Labels (mit einigen Vorbehalten, wie sie mit verschachtelten switch-Anweisungen funktionieren). Insbesondere definieren sie nicht selbst Blöcke für die Anweisungen, von denen Sie glauben, dass sie “innerhalb des Falls” liegen, und Sie können sie verwenden, um in die Mitte eines Blocks zu springen, genau wie Sie es mit einem goto tun könnten. Beim Springen in die Mitte eines Blocks gelten die gleichen Einschränkungen wie bei goto bezüglich des Überspringens der Initialisierung von Variablen usw.

Abgesehen davon ist es in der Praxis wahrscheinlich klarer, dies mit einer goto-Anweisung zu schreiben, wie in:

    switch (i) {
    case 0:
        putchar('a');
        goto case2;
    case 1:
        putchar('b');
        // @fallthrough@
    case2:
    case 2:
        putchar('c');
        break;
    }

  • “Beim Springen in die Mitte eines Blocks gelten die gleichen Einschränkungen wie bei goto bezüglich des Überspringens der Initialisierung von Variablen usw.” Welche Vorbehalte wären das? Du kann nicht Überspringen Sie die Initialisierung von Variablen.

    – Asteroiden mit Flügeln

    22. Oktober 2020 um 15:45 Uhr

  • @AsteroidsWithWings, meinst du “kann nicht” aus einer standardkonformen Perspektive oder aus der praktischen Perspektive, die ein Compiler nicht zulässt? Weil mein GCC es im C-Modus mit einer Warnung zulässt. Es erlaubt es jedoch nicht im C++-Modus.

    – Ilkkachu

    22. Oktober 2020 um 16:47 Uhr

  • @quetzalcoatl gcc-9 und clang-6 erlauben beide diesen Code und warnen vor potenziell nicht initialisiert bee (im C-Modus; in C++ kommt es zu einem Fehler bei “Kann nicht von der Switch-Anweisung zu diesem Fall springen. Beschriftung/Sprung umgeht die Variableninitialisierung”).

    – Ruslan

    22. Oktober 2020 um 18:56 Uhr


  • Sie wissen, dass Sie bei der Verwendung etwas falsch machen goto ist die sauberere Lösung.

    – Konrad Rudolf

    23. Oktober 2020 um 16:24 Uhr

  • @KonradRudolph: goto wird viel verleumdet, aber daran ist wirklich nichts auszusetzen, wenn man nicht manuell komplexe Kontrollstrukturen daraus macht. Das ganze “Goto gilt als schädlich” betraf das Schreiben von HLL-Code (z. B. C), der wie ein von einem Compiler ausgegebener asm aussieht (jmps überall hin und her). Es gibt viele gut strukturierte Verwendungen von goto wie forward-only (konzeptionell nicht anders als break oder früh return), unwahrscheinlich-retry-loop-only (vermeidet das Verdecken des gemeinsamen Flusspfads und kompensiert das Fehlen von verschachtelten-continue), etc.

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

    23. Oktober 2020 um 18:50 Uhr

Ja, das ist erlaubt, und es tut, was Sie wollen. Für ein switch -Anweisung, dem C++-Standard sagt:

case- und default-Labels ändern an sich nichts am Kontrollfluss, der sich ungehindert über solche Labels hinweg fortsetzt. Um einen Schalter zu verlassen, siehe break.

[Note 1: Usually, the substatement that is the subject of a switch is compound and case and default labels appear on the top-level statements contained within the (compound) substatement, but this is not required. Declarations can appear in the substatement of a switch statement. — end note]

Also wenn die if -Anweisung ausgewertet wird, verläuft der Kontrollfluss nach den Regeln einer an if Anweisung, unabhängig von dazwischenliegenden Fallbeschriftungen.

  • Als konkreten Fall kann man sich das anschauen Boost.Coroutinen für eine Reihe von Makros, die sich den Magen umdrehen und diese Regel (und ein halbes Dutzend anderer Eckfälle von C++) nutzen, um Coroutinen mit C++03-Regeln zu implementieren. Sie wurden erst in C++20 Teil der Sprache, aber diese Makros haben dafür gesorgt, dass sie funktionieren … solange Sie zuerst Ihre Antazida genommen haben!

    – Cort Ammon

    22. Oktober 2020 um 20:49 Uhr


Benutzeravatar von Pete Becker
Peter Becker

Wie andere Antworten erwähnt haben, ist dies vom Standard technisch erlaubt, aber für zukünftige Leser des Codes sehr verwirrend und unklar.

Deshalb switch ... case Anweisungen sollten normalerweise mit Funktionsaufrufen und nicht mit viel Inline-Code geschrieben werden.

switch(i) {
case 0:
    do_zero_case(); do_general_stuff(); break;
case 1:
    do_one_case(); do_general_stuff(); break;
case 2:
    do_general_stuff(); break;
default:
    do_default_not_zero_not_one_not_general_stuff(); break;
}

  • Beachten Sie, dass der Code in der Frage dies nicht tut do_general_stuff für Vorgabe, nur für Fall 2 (und 0 und 1). Es läuft nie der Schalter für eine i jedoch außerhalb des Bereichs 0..2.

    – Peter Cordes

    23. Oktober 2020 um 0:52 Uhr

  • @PeterCordes – beachten Sie, dass der Code in der Antwort dies nicht tut do_general_stuff für Standard nur für Fall 2 (und 0 und 1).

    – Peter Becker

    23. Oktober 2020 um 12:53 Uhr

  • Ein Vorschlag – vielleicht sollte der Standardfall entfernt oder umbenannt werden? Es ist ziemlich einfach, die verschiedenen Funktionsnamen zu übersehen.

    – sehen

    23. Oktober 2020 um 14:29 Uhr

  • @PeteBecker: Oh, IDK, wie ich das vermisst habe general und default waren andere Wörter. OTOH, es ist eine bekannte Tatsache, dass Menschen normalerweise immer noch ein Wort lesen können, wenn die mittleren Buchstaben verschlüsselt sind; Wir neigen dazu, den Anfang und das Ende zu betrachten, daher ist es wahrscheinlich nicht ideal für die Skimmbarkeit, wenn nur die Mitte unterschiedlich ist. Eventuell entfernen do_ Präfix.

    – Peter Cordes

    23. Oktober 2020 um 22:49 Uhr

  • @supercat: Bei dieser “bekannten Tatsache” geht es nicht darum, die mittleren Buchstaben durch andere zu ersetzen, sondern nur darum, die Reihenfolge von damals zu verwechseln. „Wenn der cialm in graneel wahr wäre, würdest du albe to raed tihs sein.“

    – Michael Kärcher

    24. Oktober 2020 um 19:41 Uhr

1423020cookie-checkSoll die Verwendung von if (0) zum Überspringen eines Falls in einem Schalter funktionieren?

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

Privacy policy