mache {…} while (false)

125

Ich habe mir einen Code von einer Person angesehen und festgestellt, dass er ein Muster in seinen Funktionen zu haben scheint:

<return-type> function(<params>)
{
 <initialization>

 do
 {
   <main code for function>
 }
 while(false);

 <tidy-up & return>
}

Es ist nicht schlecht , eigentümlicher (der eigentliche Code ist ziemlich ordentlich und nicht überraschend). Das habe ich noch nie gesehen und ich habe mich gefragt, ob sich jemand eine Logik dahinter vorstellen kann - Hintergrund vielleicht in einer anderen Sprache?

Mr. Boy
quelle
2
Haben Sie versucht, es auf die "normale" Version zu ändern und zu prüfen, ob es noch kompiliert wird? Wenn es beim normalen Schreiben nicht kompiliert werden kann, kann der Compilerfehler einen Hinweis darauf geben, warum dies getan wurde.
Mark Byers
11
Warum fragen Sie nicht den "Einzelnen", anstatt uns nach Vermutungen über seine Absicht zu fragen?
1
Vielleicht hat sein Professor verlangt, dass er a verwendet, do...while()und seitdem hat er diesen Code als Funktionsvorlage verwendet.
Hogan
6
@Neil ... Ich möchte vermeiden, dumm auszusehen, wenn ich ihn konfrontiere, und er erklärt die hochentwickelte C ++ - Verwendung, an die ich nie gedacht habe.
Mr. Boy
6
Es scheint bereits einige unterschiedliche Antworten zu geben. Können Sie den ursprünglichen Autor nach seinen Absichten fragen und zurückschicken?
Nevets1219

Antworten:

174

Sie können breakaus do{...}while(false).

Thomas Eding
quelle
95
+1, weil das wahrscheinlich die Absicht des Codes ist, aber so etwas zu tun ist nur ein idiotisch getarnter Schritt. Wenn Sie denken, dass ein goto das richtige Werkzeug für den Job ist, sollten Sie einfach ein # $ (* # @ goto.
dsimcha
55
Es ist mehr als ein getarnter Goto. Es ist ein eingeschränktes (strukturiertes) goto.
Thomas Eding
19
Inwiefern ist es "eingeschränkt"? Nur vorwärts zu springen ist kaum eine "Einschränkung". Ein goto ist ein goto, und es ist schlimmer, einen anzuziehen, damit es so aussieht, als wäre es keiner, als nur einen goto zu verwenden.
Anon.
44
@Anon.: Vorwärts springen ist eine Einschränkung für ein Goto, und Herausspringen ist definitiv eine Einschränkung. Das eigentliche Problem bei gotos ist der Spaghetti-Code und ein so großer Vorwärts- und Rückwärtssprung.
David Thornley
33
Eine tatsächliche Schleife ist semantisch kein Goto. Eine Bedingung ist semantisch kein goto. "Gehen Sie zum Ende der Funktion und führen Sie den Bereinigungscode aus" ist semantisch ein Goto. Verwenden Sie gotos, wenn die Semantik gilt, verkleiden Sie sich nicht semantisch anders, weil Sie Angst vor ihnen haben.
Anon.
125

Viele Leute weisen darauf hin, dass es oft mit Pause als unangenehme Art des Schreibens von "goto" verwendet wird. Das ist wahrscheinlich wahr, wenn es direkt in die Funktion geschrieben ist.

In einem Makro ist OTOH do { something; } while (false)eine bequeme Möglichkeit, ein Semikolon nach dem Makroaufruf zu erzwingen. Es darf absolut kein anderes Token folgen.

Eine andere Möglichkeit besteht darin, dass dort entweder einmal eine Schleife vorhanden war oder dass in Zukunft eine Iteration hinzugefügt werden soll (z. B. in der testgetriebenen Entwicklung war keine Iteration erforderlich, um die Tests zu bestehen, aber logischerweise wäre es sinnvoll, dort eine Schleife durchzuführen, wenn Die Funktion musste etwas allgemeiner sein als derzeit erforderlich.

Ben Voigt
quelle
18
+1 für die Erwähnung der Nützlichkeit in Makros; Ich bin überrascht, dass das sonst niemand erwähnt hat!
Nick Meyer
7
Ja, die Makrosache ist tatsächlich eine vollkommen gültige Verwendung davon. Außerhalb von Makros ist es natürlich nur albern ...;)
Jalf
12
Es ist nicht umständlich, es ist nützlich, weil es ein Scoped- Goto ist - es bedeutet, dass alle Variablen, die Sie in der do-Schleife deklarieren, zerstört werden, während das Goto dies nicht tut.
Ana Betts
9
@Paul: Nichts hindert Sie daran, eine Reihe von Anweisungen in Klammern zu setzen, um dieses Verhalten mit goto zu erzwingen.
Erikkallen
5
@Paul: Wenn Sie einen Block verlassen, werden lokale Variablen in C ++ definitiv zerstört, genau wie break. Und in C werden Variablen nicht wirklich zerstört, sondern ihr Stapelspeicher wird einfach zurückgefordert.
Ben Voigt
25

Die Pause als goto ist wahrscheinlich die Antwort, aber ich werde eine andere Idee vorbringen.

Vielleicht wollte er lokal definierte Variablen haben und benutzte dieses Konstrukt, um einen neuen Bereich zu erhalten.

Denken Sie daran {...}, dass dies in C ++ zwar überall möglich ist, dies jedoch nicht immer der Fall war.

Hogan
quelle
30
In diesem Fall hätte er auch geschweifte Klammern verwenden können.
Nemanja Trifunovic
2
@Nemanja, Sie wären überrascht, wie viele Entwickler das nicht wissen und etwas ausprobieren, das mit dem zusammenhängt, was Hogan vorschlägt
Polaris878
8
@Polaris @Nemanja, erst als ich 4 Jahre lang in C / C ++ programmiert hatte, fand ich heraus, dass Sie {} überall einen neuen lokalen Bereich erstellen können . Dies ist besonders praktisch im switch-caseCode
Earlz
1
@Nemanja: Ich weiß nicht genau wann, aber ich bin mir sicher, dass {...}irgendwo eine modernere Entwicklung in C ++ ist (denken Sie daran, dass das erste C ++, das ich verwendet habe, mit einem Vorprozessor implementiert wurde und diese moderne Verwendung nicht zuließ.) Vielleicht Der Autor war einfach so altmodisch.
Hogan
2
Wann waren nicht überall Zahnspangen erlaubt? Ich habe wie vor 15 Jahren mit dem Programmieren begonnen und es war damals erlaubt (sowohl in meinem Lehrbuch als auch in jedem Compiler, den ich ausprobiert habe).
Erikkallen
20

Ich habe gesehen, dass es als nützliches Muster verwendet wird, wenn es viele potenzielle Austrittspunkte für die Funktion gibt, aber immer der gleiche Bereinigungscode erforderlich ist, unabhängig davon, wie die Funktion beendet wird.

Es kann einen lästigen if / else-if-Baum viel einfacher zu lesen machen, indem es einfach brechen muss, wenn ein Austrittspunkt erreicht wird, und der Rest der Logik anschließend inline ist.

Dieses Muster ist auch in Sprachen nützlich, die keine goto-Anweisung haben. Vielleicht hat der ursprüngliche Programmierer dort das Muster gelernt.

Cameron
quelle
15
Verwenden Sie dann einfach ein ehrliches, unkompliziertes Goto anstelle eines dünn getarnten Goto.
Dsimcha
4
Ich mag diesen Weg besser. Es ist leicht zu lesen und trägt nicht das Stigma eines Goto.
Cameron
8
gotos sind leicht zu lesen, wenn sie sparsam und lokal verwendet werden. Sie bekamen ihr Stigma von hinten, als sie die Hauptform der Flusskontrolle waren und über Hunderte von Linien sprangen.
Dsimcha
13
Die Verwendung von goto wegen eines "Stigmas" ist ein sicheres Zeichen für eine Frachtkult-Programmierung.
Anon.
6
Ganz zu schweigen davon, dass eine Menge Bereinigungscode ein schlechter Geruch ist. Da es sich um C ++ handelt, sollte sich der Bereinigungscode normalerweise in Destruktoren befinden, die während der RAII-Verarbeitung aufgerufen werden.
David Thornley
12

Ich habe solchen Code gesehen, damit Sie ihn breakals eine gotoArt verwenden können.

Brian Young
quelle
10

Dies ist nur eine Perversion von while, um die Sematik zu erhalten, goto tidy-upohne das Wort goto zu verwenden.

Es ist schlechter Stil, weil wenn Sie andere Schleifen innerhalb des äußeren whileder breaksden Leser zweideutig worden. "Soll das zum Ausgang gehen? Oder soll das nur aus der inneren Schleife ausbrechen?"

John Knoeller
quelle
10

Ich denke , es ist bequemer zu schreiben breakstatt goto end. Sie müssen sich nicht einmal einen Namen für das Etikett ausdenken, was die Absicht klarer macht: Sie möchten nicht zu einem Etikett mit einem bestimmten Namen springen. Du willst hier raus.

Es besteht auch die Möglichkeit, dass Sie die Zahnspange sowieso benötigen. Das ist also die do{...}while(false);Version:

do {
   // code
   if (condition) break; // or continue
   // more code
} while(false);

Und so müssten Sie es ausdrücken, wenn Sie Folgendes verwenden möchten goto:

{
   // code
   if (condition) goto end;
   // more code
}
end:

Ich denke, die Bedeutung der ersten Version ist viel einfacher zu verstehen. Außerdem ist es einfacher zu schreiben, einfacher zu erweitern, einfacher in eine Sprache zu übersetzen, die nicht unterstützt gotowird usw.


Die am häufigsten erwähnte Sorge über die Verwendung von breakist, dass es eine schlecht getarnte ist goto. Hat aber tatsächlich breakmehr Ähnlichkeit mit return: Beide Anweisungen springen aus einem Codeblock, der im Vergleich zu ziemlich strukturiert ist goto. Trotzdem erlauben beide Anweisungen mehrere Austrittspunkte in einem Codeblock, was manchmal verwirrend sein kann. Immerhin würde ich versuchen, die klarste Lösung zu finden, was auch immer das in der spezifischen Situation ist.

Robert
quelle
Oh, ich habe gerade bemerkt, dass ich die Frage vor der Beantwortung nicht vollständig verstanden habe. Ich dachte, es geht um die Verwendung von do{ ... }while(false);im Allgemeinen. Aber eigentlich geht es darum, damit eine Art zu emulieren try{ ... }finally{ ... }.
Robert
Gute Beobachtung der Parallele zwischen Pause und Rückkehr, anstatt zu gehen. Stimmen Sie zu, dass die klarste Lösung die beste ist. In vielen Fällen ist es wahrscheinlich klarer, Code wie diesen in einer eigenen separaten Funktion zu kapseln, die return anstelle von break verwendet, und dann die Bereinigung in der aufrufenden Funktion durchzuführen.
Persiflage
7

Dieser Trick wird von Programmierern verwendet, die zu schüchtern sind, um einen expliziten gotoCode zu verwenden. Der Autor des obigen Codes wollte die Möglichkeit haben, direkt von der Mitte des Codes zum Punkt "Bereinigen und Zurückgeben" zu springen. Aber sie wollten kein Etikett verwenden und explizit goto. Stattdessen können sie einen breakim Körper des obigen "falschen" Zyklus verwenden, um den gleichen Effekt zu erzielen.

Ameise
quelle
7

Es ist eine sehr verbreitete Praxis. In C . Ich versuche es mir so vorzustellen, als ob du dich selbst belügen willst, "ich benutze kein goto". Wenn man darüber nachdenkt, wäre an einem gotoähnlichen Gebrauch nichts auszusetzen . Tatsächlich würde dies auch die Einrückungsstufe verringern.

Ich bemerkte jedoch, dass diese do..whileSchleifen sehr oft wachsen. Und dann bekommen sie ifs und elses hinein und machen den Code tatsächlich nicht sehr lesbar, geschweige denn testbar.

Diese do..whilesind normalerweise für eine Bereinigung vorgesehen . Auf jeden Fall würde ich es vorziehen, RAII zu verwenden und frühzeitig von einer kurzen Funktion zurückzukehren. Auf der anderen Seite bietet C Ihnen nicht so viele Annehmlichkeiten wie C ++ , was einen do..whileder besten Ansätze für eine Bereinigung darstellt.

Dmitry
quelle
6

Es sieht aus wie ein C-Programmierer. In C ++ verfügen automatische Variablen über Destruktoren, die Sie zum Bereinigen verwenden. Daher sollte vor der Rückgabe nichts aufgeräumt werden müssen. In C hatten Sie dieses RAII- Idiom nicht. Wenn Sie also allgemeinen Bereinigungscode haben, haben Sie es auchgoto oder verwenden Sie eine Durchlaufschleife wie oben.

Sein Hauptnachteil gegenüber der C ++ - Sprache ist, dass es nicht aufräumt, wenn eine Ausnahme in den Körper geworfen wird. C hatte keine Ausnahmen, daher war dies kein Problem, aber es macht es zu einer schlechten Angewohnheit in C ++.

Pete Kirkham
quelle
4

Mehrere Erklärungen. Der erste ist allgemein, der zweite ist spezifisch für C-Präprozessor-Makros mit folgenden Parametern:

Ablaufsteuerung

Ich habe gesehen, dass dies in einfachem C-Code verwendet wird. Grundsätzlich ist es eine sicherere Version von goto, da Sie daraus ausbrechen können und der gesamte Speicher ordnungsgemäß bereinigt wird.

Warum sollte so etwas gotogut sein? Wenn Sie einen Code haben, in dem so gut wie jede Zeile einen Fehler zurückgeben kann, Sie jedoch auf alle gleich reagieren müssen (z. B. indem Sie den Fehler nach der Bereinigung Ihrem Anrufer übergeben), ist dies normalerweise besser lesbar, um einen Fehler zu vermeidenif( error ) { /* cleanup and error string generation and return here */ } als Es vermeidet das Duplizieren von Bereinigungscode.

In C ++ gibt es jedoch Ausnahmen + RAII für genau diesen Zweck, daher würde ich es als schlechten Codierungsstil betrachten.

Semikolonprüfung

Wenn Sie das Semikolon nach einem funktionsähnlichen Makroaufruf vergessen, werden Argumente möglicherweise unerwünscht kontrahiert und in eine gültige Syntax kompiliert. Stellen Sie sich das Makro vor

#define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo");

Das nennt man versehentlich als

if( foo )
    PRINT_IF_DEBUGMODE_ON("Hullo\n")
else
    doSomethingElse();

Das "else" wird als mit dem verknüpft betrachtet gDebugModeOn, also wann fooistfalse , geschieht genau das Gegenteil von dem, was beabsichtigt war.

Bereitstellung eines Bereichs für temporäre Variablen.

Da das do / while geschweifte Klammern hat, haben temporäre Variablen einen klar definierten Bereich, dem sie nicht entkommen können.

Vermeiden Sie "möglicherweise unerwünschte Semikolon" -Warnungen

Einige Makros werden nur in Debug-Builds aktiviert. Sie definieren sie wie folgt:

#if DEBUG
#define DBG_PRINT_NUM(n) printf("%d\n",n);
#else
#define DBG_PRINT_NUM(n) 
#endif

Wenn Sie dies nun in einem Release-Build innerhalb einer Bedingung verwenden, wird es kompiliert

if( foo )
    ;

Viele Compiler sehen dies als dasselbe an wie

if( foo );

Was oft versehentlich geschrieben wird. Sie erhalten also eine Warnung. Das do {} while (false) verbirgt dies vor dem Compiler und wird von ihm als Hinweis darauf akzeptiert, dass Sie es wirklich sind nichts tun möchten.

Vermeiden der Erfassung von Linien durch Bedingungen

Makro aus vorherigem Beispiel:

if( foo )
    DBG_PRINT_NUM(42)
doSomething();

In einem Debug-Build wird dies nun problemlos kompiliert, da wir gewöhnlich auch das Semikolon eingefügt haben. Im Release-Build wird daraus jedoch plötzlich:

if( foo )

doSomething();

Oder klarer formatiert

if( foo )
    doSomething();

Welches ist überhaupt nicht das, was beabsichtigt war. Durch Hinzufügen eines do {...} while (false) um das Makro wird das fehlende Semikolon in einen Kompilierungsfehler umgewandelt.

Was bedeutet das für das OP?

Im Allgemeinen möchten Sie Ausnahmen in C ++ für die Fehlerbehandlung und Vorlagen anstelle von Makros verwenden. In dem sehr seltenen Fall, in dem Sie noch Makros benötigen (z. B. beim Generieren von Klassennamen mithilfe des Einfügens von Token) oder auf einfaches C beschränkt sind, ist dies ein nützliches Muster.

uliwitness
quelle
In Bezug auf das Scoping benötigen Sie den Schleifenapparat nicht. Ein "schmuckloser" Block ist legal : x =1; y = 2; { int tmp = y; y = x; x = tmp; }.
Chepner
Ein schmuckloser Block erzwingt jedoch nicht das Vorhandensein eines fehlenden Semikolons, was unerwünschte Nebenwirkungen haben könnte. Do {} while (); ERFORDERT das Semikolon und führt zB nicht dazu, dass folgende Anweisungen in ein "if" gezogen werden, wenn das Makro in einem Release-Build wie in meinem obigen Beispiel nichts definiert.
uliwitness
Könnte dies nicht vermieden werden, indem einfach eine bessere Definition für das Makro bereitgestellt wird? #define DBG_PRINT_NUM(n) {}.
Chepner
1
Nein, weil {} eine vollständige Anweisung ist, dh einem ";" entspricht. Wenn Sie also schreiben, if(a) DBG_PRINT_NUM(n); else other();wird es nicht kompiliert. Nur if(a) {} else other();oder if(a) ; else other();ist gültig, aber if(a) {}; else other();nicht, da die "if" -Klausel dadurch aus zwei Anweisungen besteht.
uliwitness
2

Vielleicht wird es verwendet, damit breakes im Inneren verwendet werden kann, um die Ausführung von weiterem Code jederzeit abzubrechen:

do {
    if (!condition1) break;
    some_code;
    if (!condition2) break;
    some_further_code;
    // …
} while(false);
Gumbo
quelle
7
Dies scheint ein Versuch zu sein, die Verwendung zu vermeiden, gotonur weil jemand gehört hat, dass es "schlecht" ist.
Anon.
1
Vielleicht, aber C ++ hat aus genau diesem Grund Ausnahmen.
TED
2

Ich denke, dies wird getan, um breakoder continueAussagen zu verwenden. Eine Art "goto" -Codelogik.

Ivan Nevostruev
quelle
2

Es ist ganz einfach: Anscheinend können Sie mit der breakAnweisung jederzeit aus der gefälschten Schleife springen . Darüber hinaus ist der doBlock ein separater Bereich (der auch nur mit erreicht werden könnte { ... }).

In einer solchen Situation ist es möglicherweise besser, RAII zu verwenden (Objekte, die automatisch automatisch zerstört werden, wenn die Funktion endet). Ein anderes ähnliches Konstrukt ist die Verwendung von goto- ja, ich weiß, dass es böse ist , aber es kann verwendet werden, um allgemeinen Bereinigungscode wie folgt zu haben:

<return-type> function(<params>)
{
 <initialization>

 <main code for function using "goto error;" if something goes wrong>

 <tidy-up in success case & return>

 error:

 <commmon tidy-up actions for error case & return error code or throw exception>
}

(Nebenbei: Das Konstrukt do-while-false wird in Lua verwendet, um die fehlende continueAnweisung zu ermitteln.)

AndiDog
quelle
2

Viele Antwortende gaben den Grund dafür an do{(...)break;}while(false). Ich möchte das Bild durch ein weiteres Beispiel aus der Praxis ergänzen.

Im folgenden Code musste ich den Enumerator operationbasierend auf der Adresse festlegen , auf die der dataZeiger zeigt. Da ein Switch-Case zunächst nur für Skalartypen verwendet werden kann, habe ich dies auf diese Weise ineffizient durchgeführt

if (data == &array[o1])
    operation = O1;
else if (data == &array[o2])
    operation = O2;
else if (data == &array[on])
    operation = ON;

Log("operation:",operation);

Da sich Log () und der Rest des Codes für einen beliebigen Operationswert wiederholen, habe ich mir überlegt, wie ich den Rest der Vergleiche überspringen kann, wenn die Adresse bereits gefunden wurde. Und hier do{(...)break;}while(false)kommt es zum Einsatz.

do {
    if (data == &array[o1]) {
        operation = O1;
        break;
    }
    if (data == &array[o2]) {
        operation = O2;
        break;
    }
    if (data == &array[on]) {
        operation = ON;
        break;
    }
} while (false);

Log("operation:",operation);

Man mag sich fragen, warum er breakin einer ifAussage nicht dasselbe tun konnte , wie:

if (data == &array[o1])
{
    operation = O1;
    break;
}
else if (...)

breakwirkt ausschließlich mit der nächsten einschließenden Schleife oder Schaltern, ob es eine sein for, whileoder do .. whileArt, so dass leider nicht funktionieren.

4pie0
quelle
1

Wie alt war der Autor?

Ich frage, weil ich in den späten 80ern einmal auf einen Echtzeit-Fortran-Code gestoßen bin, der das getan hat. Es stellt sich heraus, dass dies eine wirklich gute Möglichkeit ist, Threads auf einem Betriebssystem zu simulieren, auf dem sie nicht vorhanden sind. Sie setzen einfach das gesamte Programm (Ihren Scheduler) in eine Schleife und rufen Ihre "Thread" -Routinen nacheinander auf. Die Thread-Routinen selbst sind Schleifen, die iterieren, bis eine von mehreren Bedingungen eintritt (häufig eine bestimmte Menge von Zeit ist vergangen). Es ist "kooperatives Multitasking", da es an den einzelnen Threads liegt, die CPU von Zeit zu Zeit aufzugeben, damit die anderen nicht verhungern. Sie können die Schleifen-Unterprogrammaufrufe verschachteln, um die Thread-Priorität zu simulieren Bands.

TED
quelle
0

Ich stimme den meisten Postern hinsichtlich der Verwendung als dünn getarntes Goto zu. Makros wurden auch als mögliche Motivation für das Schreiben von Code im Stil erwähnt.

Ich habe dieses Konstrukt auch in gemischten C / C ++ - Umgebungen als Ausnahme eines armen Mannes gesehen. Das "do {} while (false)" mit einem "break" kann verwendet werden, um zum Ende des Codeblocks zu springen, falls in der Schleife etwas auftritt, das normalerweise eine Ausnahme rechtfertigt.

Ich habe dieses Konstrukt auch in Geschäften verwendet, in denen die Ideologie "Single Return per Function" durchgesetzt wird. Dies ist wiederum anstelle eines expliziten "goto" - aber die Motivation besteht darin, mehrere Rückgabepunkte zu vermeiden, den Code nicht zu "überspringen" und die tatsächliche Ausführung innerhalb dieser Funktion fortzusetzen.

Stan Graves
quelle
0

Ich arbeite mit Adobe InDesign SDK, und in den InDesign SDK-Beispielen ist fast jede Funktion so geschrieben. Es liegt an der Tatsache, dass die Funktionen normalerweise sehr lang sind. Wo Sie QueryInterface (...) ausführen müssen, um etwas aus dem Anwendungsobjektmodell abzurufen. Also normalerweise folgt auf jedes QueryInterface ifnicht gut gelaufen , Pause.

Kugel
quelle
0

Viele haben bereits die Ähnlichkeit zwischen diesem Konstrukt und a angegeben gotound eine Präferenz für das goto zum Ausdruck gebracht. Vielleicht beinhaltete der Hintergrund dieser Person eine Umgebung, in der Gotos durch Codierungsrichtlinien strengstens verboten waren?

Mark Ransom
quelle
0

Der andere Grund, an den ich denken kann, ist, dass es die Zahnspangen verziert , während ich glaube, dass nackte Zahnspangen mit einem neueren C ++ - Standard nicht in Ordnung sind (ISO C mag sie nicht). Ansonsten einen statischen Analysator wie Flusen zu beruhigen.

Sie sind sich nicht sicher, warum Sie sie möchten, möglicherweise einen variablen Bereich oder einen Vorteil mit einem Debugger.

Siehe Trivial Do While-Schleife , und Klammern sind ab C2 gut.

Um meine Terminologie zu verdeutlichen (von der ich glaube, dass sie der Standardverwendung folgt):

Nackte Zahnspangen :

init();
...
{
c = NULL;
mkwidget(&c);
finishwidget(&c);
}
shutdown();

Leere Zahnspangen (NOP):

{}

z.B

while (1)
   {}  /* Do nothing, endless loop */

Block :

if (finished)
{
     closewindows(&windows);
     freememory(&cache);
}

das würde werden

if (finished)
     closewindows(&windows);
freememory(&cache);

Wenn die geschweiften Klammern entfernt werden, ändert sich nicht nur der Umfang lokaler Variablen, sondern auch der Ausführungsfluss. Also nicht "freistehend" oder "nackt".

Nackte Klammern oder ein Block können verwendet werden, um jeden Codeabschnitt zu kennzeichnen, der möglicherweise für eine (Inline-) Funktion geeignet ist, die Sie markieren möchten, aber zu diesem Zeitpunkt nicht umgestalten.

mctylr
quelle
"Ja wirklich?" Das ist eine Schande. Eines der wenigen Dinge, die mir an C gefallen haben, war, dass Sie damit fast überall einen neuen verschachtelten Bereich deklarieren konnten.
TED
1
Nackte Zahnspangen sind gelegentlich hilfreich, um seltsame Abstürze zu beheben. Siehe blogs.msdn.com/oldnewthing/archive/2004/05/20/135841.aspx .
Brian
1
In C ++ kann ein Block (dh Ihre "nackten Klammern") überall dort verwendet werden, wo eine einzelne Anweisung zulässig wäre.
Ben Voigt
@BenVoigt Leere Klammern, dh ein NOP unterscheidet sich von einer "nackten Klammer", bei der es sich um einen Block handelt, der um eine lineare Folge von Anweisungen hinzugefügt wird. ZB `printf (" Hallo "); {putchar (','); Putchar (0x20); } printf ("world! \ n"); `wobei die geschweiften Klammern nicht Teil der Schleifen- oder Verzweigungssteuerung sind.
mctylr
@mctylr: Ich habe nicht über leere Zahnspangen gesprochen.
Ben Voigt
0

Es ist eine erfundene Art, a zu emulieren, GOTOda diese beiden praktisch identisch sind:

// NOTE: This is discouraged!
do {
    if (someCondition) break;
    // some code be here
} while (false);
// more code be here

und:

// NOTE: This is discouraged, too!
if (someCondition) goto marker;
// some code be here
marker:
// more code be here

Auf der anderen Seite sollten beide wirklich mit ifs gemacht werden:

if (!someCondition) {
    // some code be here
}
// more code be here

Obwohl die Verschachtelung etwas hässlich werden kann, wenn Sie nur eine lange Folge von Forwards GOTOin verschachtelte ifs verwandeln . Die eigentliche Antwort ist jedoch das richtige Refactoring, ohne archaische Sprachkonstrukte zu imitieren.

Wenn Sie verzweifelt versuchen würden, einen Algorithmus mit GOTOs darin zu transliterieren , könnten Sie dies wahrscheinlich mit dieser Redewendung tun. Es ist sicherlich nicht standardisiert und ein guter Indikator dafür, dass Sie sich nicht genau an die erwarteten Redewendungen der Sprache halten.

Mir ist keine C-ähnliche Sprache bekannt, in der do / while eigentlich eine idiomatische Lösung für irgendetwas ist.

Sie könnten wahrscheinlich das ganze Durcheinander in etwas Vernünftigeres umgestalten, um es idiomatischer und viel lesbarer zu machen.

Alan Plum
quelle
1
Alan, do..while(false)ist nicht dafür gedacht, "undefiniert" mehrmals zu laufen, sondern dafür, einmal zu laufen .
Dmitry
2
Es wird undefiniert oft ausgeführt, wenn Sie continueam Ende eine bedingte Anweisung haben, wie ich gezeigt habe. Damit undefinedmeine ich einfach, dass Sie nicht wissen, ob es mehr als einmal ausgeführt wird, es sei denn, Sie können vorhersagen, ob die Bedingungen bei einer bestimmten Iteration erfüllt werden. Ohne continues drin, do..while(false)läuft einmal, aber auch ohne breaks drin, while(true)läuft immer, so dass das „Standardverhalten“ nicht wirklich relevant ist , zu verstehen , was kann mit den Schleifen erfolgen.
Alan Plum
Dies ist nützlich, wenn Sie ein Makro mit mehreren Anweisungen definieren. Wenn Sie sie in ein do / while (false) einschließen, können Sie das Makro in einer if / else-Anweisung wie in jedem anderen Funktionsaufruf verwenden. Eine Erklärung finden Sie unter noveltheory.com/TechPapers/while.htm
John Paquin,
5
Jemand versteht die Semantik von nicht continue. continuewiederholt NICHT die Schleife. "Die continueAnweisung darf nur in einer Iterationsanweisung vorkommen und bewirkt, dass die Steuerung an den Schleifenfortsetzungsabschnitt der kleinsten einschließenden Iterationsanweisung übergeben wird, dh an das Ende der Schleife."
Ben Voigt
-1, die beiden obersten Aussagen sind aus den Gründen, auf die @BenVoigt hinweist, nicht identisch.
cmh
0

Einige Codierer bevorzugen nur einen einzigen Exit / Return von ihren Funktionen. Die Verwendung eines Dummys do {....} while (false); ermöglicht es Ihnen, aus der Dummy-Schleife auszubrechen, wenn Sie fertig sind und immer noch eine einzige Rückkehr haben.

Ich bin ein Java-Codierer, also wäre mein Beispiel so etwas wie

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class p45
{
    static List<String> cakeNames = Arrays.asList("schwarzwald torte", "princess", "icecream");
    static Set<Integer> forbidden = Stream.of(0, 2).collect(Collectors.toSet());

    public static  void main(String[] argv)
    {
        for (int i = 0; i < 4; i++)
        {
            System.out.println(String.format("cake(%d)=\"%s\"", i, describeCake(i)));
        }
    }


    static String describeCake(int typeOfCake)
    {
        String result = "unknown";
        do {
            // ensure type of cake is valid
            if (typeOfCake < 0 || typeOfCake >= cakeNames.size()) break;

            if (forbidden.contains(typeOfCake)) {
                result = "not for you!!";
                break;
            }

            result = cakeNames.get(typeOfCake);
        } while (false);
        return result;
    }
}
David Young
quelle
0

In solchen Fällen benutze ich

switch(true) {
   case condution1:
      ...
      break;
   case condution2:
      ...
      break;
}
Женя Остроушко
quelle
-1

Das ist amüsant. Es gibt wahrscheinlich Unterbrechungen innerhalb der Schleife, wie andere gesagt haben. Ich hätte es so gemacht:

while(true)
{
   <main code for function>
   break; // at the end.
}
fastcodejava
quelle
2
Mit dem Potenzial, für immer zu schleifen? do..while(false)Ausgänge immer, while(true)ist eher riskant.
Dmitry
1
while(true)ist die richtige Sprache in den meisten Sprachen. Sie finden es häufig in GUI-Anwendungen als Hauptschleife des Programms. Da Sie im Grunde davon ausgehen, dass das Programm nicht sterben sollte, bis es dazu aufgefordert wird, do..while(false)würde a alle Arten von erfundener Logik verursachen. Dieser Ansatz mag von einem perfektionistischen POV risikoreicher sein, ist jedoch semantisch einfacher und daher für menschliche Programmierer weniger fehleranfällig (sorry, Skynet).
Alan Plum
2
@dmitry do{...}while(false)ist genau das gleiche wiewhile(true){ .. break;}
N 1.1
4
@ N1.1: Nicht in Gegenwart von continue, sie sind nicht gleich.
Ben Voigt