Wieso ist das Programm ein Alptraum?
-
Marc++us schrieb:
Ja, seltsam, oder?
Das wird immer so gesagt, aber gerade für C++-Programmierer ist goto meines Erachtens eine gute Krücke für Exception-Handling. Wenn man den Code ohne goto schreibt hat man ewige ifs und Flags, total unübersichtlich. Dann lieber aufgeräumt ein Sprung mit goto auf den Exit-Handler...
Wer GOTO in einem C Programm braucht um den Code übersichtlich hinzukriegen, der hat generell beim Design der Software schon etwas falsch gemacht.
GOTO ist und bleibt ein nogo, das ist schlechter Programmierstil und das gilt auch für C Code.
-
Mit solch absoluten Meinungen tue ich mir eher schwer - das impliziert ja, daß man über den Vorschlag nicht mehr nachdenken muß und sich damit neuer Erkenntnisse versperrt.
Offensichtlich war goto wichtig genug um es nicht deprecated zu machen.
Einige interessante Beispiele stehen hier:
http://stackoverflow.com/questions/245742/examples-of-good-gotos-in-c-or-c
Man kann viele der klassischen Spaghetticode-Beispiele mit dem C-goto nur in pathologischen Beispielen bauen, wegen der erzwungenen Sprungbereiche.
Gerade Exit-Code gewinnt dramatisch an Klarheit durch goto, die andernfalls hilfsweise notwendigen Variablen und Abfragen führen zu weniger Klarheit.
if (!openDataFile()) goto quit; if (!getDataFromFile()) goto closeFileAndQuit; if (!allocateSomeResources) goto freeResourcesAndQuit; // Do more work here.... freeResourcesAndQuit: // free resources closeFileAndQuit: // close file quit: // quit!Aus dem verlinkten Thread. Klar, kurz, übersichtlich, keine Spaghetti.
-
Das geht auch ohne goto und es bleibt schöner strukturierter Code
int foo(....){ if (!openDataFile()) return 1; if (!getDataFromFile()) { closeFile(); return 1; /* oder 2, wer damit was machen will (z.B. Ausgabe der Fehlermeldung oder weiterführende delegierte Fehlerbehandlung etc. */ ) if (!allocateSomeResources) { freeResourcesAndQuit(); return 1; // siehe oben } // Do more work here.... freeResourcesAndQuit(); // free resources closeFile(); // close file return 0; // quit! }
-
Programmierfachprofi schrieb:
Das geht auch ohne goto
Jo.
Programmierfachprofi schrieb:
und es bleibt schöner strukturierter Code
Ne, nicht mal ansatzweise.
Sieht man schon daran, dass dein Code einen Fehler hat. Guck dir mal Zeile 13 an.
-
cooky451 schrieb:
Ne, nicht mal ansatzweise.
Sieht man schon daran, dass dein Code einen Fehler hat. Guck dir mal Zeile 13 an.Das ist nur eine Namensbenennung, die ich nicht umbenannt habe.
Also Schall und Rauch.Mach aus dem freeResourcesAndQuit(); in 13 und 18 einfach ein freeResources(); dann paßt es.
-
Und du raffst es nicht mal mit Hinweisen, da fehlt ein closeFile().
q.e.d.
-
Dieser Thread wurde von Moderator/in rapso aus dem Forum Spiele-/Grafikprogrammierung in das Forum C (C89 und C99) verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Ich halte die goto-als-RAII-Ersatz-Denkweise nicht für richtig sinnvoll -- der entstehende Code mag manchmal (wenn auch da streitbar) übersichtlicher sein als der gleiche Code mit Rückgabewertbehandlung, aber das ist weniger ein Argument für die Verwendung von goto als gegen Funktionen, die so viel Ressourcen verwalten, dass man den Überblick verliert bzw. nebenher noch massig Kram zu tun versuchen.
Im vorliegenden Fall sehe ich beispielsweise keinen Grund, *GodObject auf den Heap zu legen (der Name "GodObject" lässt eh nichts gutes vermuten, zumal es ein globaler Zeiger ist), und die Initialisierung respektive Zerstörung gehört in eine eigene Funktion. In der main selbst sollte m.E. nicht viel mehr stehen als
int main() { struct AppData app; if(InitAppData(&app)) { PlayGame(&app); DestroyAppData(&app); } else { return -1; } }InitAppData hat dann im Fehlerfall dafür zu sorgen, dass keine Aufräumarbeiten übrig bleiben. Wenn struct AppData mehrere Ressourcen verwalten muss, ist die Kaskadenproblematik damit natürlich nicht komplett entfernt, aber sie ist dann in einer Funktion isoliert, die sich um nichts anderes kümmert, und das sollte sich dann, sofern es sich nicht um ein Monsterobjekt handelt, ohne Übersichtlichkeitsprobleme regeln lassen. Wenn es sich um ein Monsterobjekt handelt, ist dieser Umstand das Problem, und das Monsterobjekt sollte in mehrere Unterstructs unter- oder aufgeteilt werden.
-
"if error goto exit" ist eines der Standardmuster in der C-Programmierung.
Vieles kann man ohne goto schöner lösen, aber manchmal sind die Alternativen einfach nur Overkill.
C ist einfach eine hässliche Sprache, und der gezielte Einsatz von goto macht sie stellenweise um eine kleine Spurschönerweniger hässlich.
-
seldon schrieb:
als der gleiche Code
Konsequent weitergedacht bedeutet das, dass keine Funktion mehr als eine Ressource reservieren darf. Das halte ich nicht immer für die beste Lösung. Somit sind wir wieder bei goto. Oder man rückt den Good-Path ein:
if (openDataFile()) { if (getDataFromFile()) { if (allocateSomeResources()) { // Do more work here... freeSomeResources(); } freeData(); } closeFile(); }Skaliert natürlich auch nicht besonders gut, aber vielleicht gut genug, um sagen zu können, dass man den Rest in Funktionen aufteilt.
-
@Programmierfachprofi:
Man sieht deutlich, daß Deine Variante semantisch gleich ist, durch die notwendige Duplizierung von Code aber fehleranfälliger und damit auch schlechter wartbar. Dein Beispiel war eigentlich wunderbar geeignet, um die Vorteile eines goto zu beleuchten.
@cooky:
Das geht natürlich, aber denk Dir das mal für Init-Abläufe bei einer hardwarenahen Sache wie einer Abfolge von DirectX-Resourcen-Anforderungen o.ä., wenn Du da jedesmal eine Funktion mit Parametern und Rückmeldewert schreibst, am Ende hast Du da ja für jede API-Funktion eine Wrapper-Funktion gebaut... und übersichtlich ist es nicht wirklich. Noch dazu weil dann sämtliche Beispiele aus der API-Doku direkt mit den API-Funktionen arbeiten, dann muß man alle Beispiele auf die eigenen Funktionen umschreiben. Da verzettelt man sich wohl relativ leicht.
Wenn man so arbeitet, wieso nimmt man nicht gleich C++ und kapselt es in Klassen. Das wäre dann konsequenter.
-
//... cleanup3(); /* TODO -> erst mal dummy, OS raeumt normalerweise eh auf */ //...@Lotte89
Ein solcher Kommentar spiegelt auch keinen guten Stil wider. Wer Resourcen allokiert sollte auch diese wieder freigeben.Ja, das OS gibt Resourcen wieder frei. Aber stell dir vor, du willst die Stelle in eine Funktion packen und weiterentwicklen (Funktion in Schleife). Dann hast du von heute auf morgen ein schönes Speicherloch.
-
Marc++us schrieb:
wenn Du da jedesmal eine Funktion mit Parametern und Rückmeldewert schreibst
Ich kann dir da nicht wirklich folgen.. den Good-Path einzurücken funktioniert immer, auch wenn man den Fehler irgendwie anders bekommt als durch einen Rückgabewert.
Marc++us schrieb:
Wenn man so arbeitet, wieso nimmt man nicht gleich C++ und kapselt es in Klassen. Das wäre dann konsequenter.
Ja sicher, ich halte C eh für obsolet, aber der Mod hat's nun mal in C Forum geschoben.

-
Da möchte ich mich als Neuling auch mal dazu einbringen. Ich halte das verhalten mancher hier, die andere gerne auf Ihren eigenen angeblich besten Programmierstil missionieren wollen für kritisch. Klar gibt es sinvolle Gründe für eine fixen Programierstil.
Bjarne Stroustrup selbst hat in einem offentlichen Vortrag selber ausdrücklich darauf hingewiesen das er selbst, das einbinden von allem und jedem in Klassen, als unmöglichen Stil empfindet, und hat darauf hingewiesen das ein Programm so einfach wie möglich, und nur so komplilziert wie nötig gestaltet werden sollte. Es gibt nun mal wirklich die Punkte wo ein 'goto' einem mehrere Zeilen mit 'if' Abfragen ersetzt und warum sollte man es nicht nutzen. Genauso ist es irgendwie sinnfei alles und jedes in eine Klasse zu packen wo eine normale Funktion das gleiche Ergebnis erzielt oder ales und jedes als Template zu schreiben. Auch die Frage nach K&R Style Braces oder Allman ist soweit sinnfrei wenn man nicht vom Arbeitgeber einen bestimmten Stil vorgeschrieben bekommt, dennoch sollte man selber bei eigenen Pogrammen bei einem Stil bleiben. Auch die frage nach Hungarian App, Hungarian Systems, oder Klarnamen Variablen Benennung ist immer wieder ein Streitpunkt hier im Forum (welches ich übrigens sehr gerne verfolge und lese). Aber bringt uns das als Community weiter? Ich denke nein. Code allgemein (ausgenommen Spaghetti Code) ist an für sich recht einfach zu lesen egal in welchem brace Stil oder Variablen Namen Konvention so lange es konsistent bei einer Form bleibt.
Just my 2 Cents.
-
Code allgemein (ausgenommen Spaghetti Code) ist an für sich recht einfach zu lesen egal in welchem brace Stil oder Variablen Namen Konvention so lange es konsistent bei einer Form bleibt.
Im Allgemeinen muss ich widersprechen. Beispielsweise folgender suaberer Code ist trotzdem schwer zu lesen (wobei ich lesen mit halbwegs verstehen gleichsetze):
int label_2lines(unsigned short const* u, unsigned short const* lu, int usize, unsigned short const* l, int lsize, unsigned short n, unsigned short* ll, unsigned short* r, int* rsize) { unsigned short const* const u_end = u + 2*usize; unsigned short const* const l_end = l + 2*lsize; int is_labeled = 0; int a = 0; while ((u < u_end) && (l < l_end)) { // overlap test if (*l < *(u+1) && *u < *(l+1)) { if (is_labeled) { // emit (lu, ll) *r = *lu; *(r+1) = *ll; r += 2; a += 2; } else { *ll = *lu; } is_labeled = 1; } // advancing if (*(u+1) < *(l+1)) { u += 2; ++lu; } else { if (!is_labeled) { *ll = n; ++n; } l += 2; ++ll; is_labeled = 0; } } // trailings spans while (l < l_end) { *ll = n; ++n; l += 2; ++ll; } *rsize = a; return n; }Und wer die Sprache nicht kennt, es ist C++!
-
Wobei hier die schwirigkeit darin besteht das der Abschnitt aus dem kontext heraus genommen ist und nicht wirklich klar wird was die einzelnen Variablen bedeuten im Programm Kontext, aber an für sich ist Zeile für Zeile recht schlüssig. Muss aber zugeben ich musste schon eine Weile lang drauf schauen.
-
Videonauth schrieb:
nciht wirklich klar wird was die einzelnen Variablen bedeuten im Programm Kontext,
Woran liegt das wirklich? Es ist nicht direkt mangelnder Kontext*. Ich könnte dir unkommentierte Abschnitte aus meinen Programmen zeigen und du wüsstest trotzdem sofort, was wofür da ist.
*: Vollständige Kenntnis des Programmes könnte helfen, aber es gibt genügend Programme bei denen aller Code so aussieht. Da hilft dir das auch nichts.
-
SeppJ schrieb:
... aber es gibt genügend Programme bei denen aller Code so aussieht. Da hilft dir das auch nichts.
Dann hilft nur noch Fleißarbeit und so viel selbst kommentieren bis es klar wird.
Aber im Grunde muss ich schon sagen 'Point taken!'. Ich mein selbst ich lerne nie aus. Gab es denn an meinen anderen Aussagen noch was zu bemängeln?
-
Videonauth schrieb:
Aber im Grunde muss ich schon sagen 'Point taken!'. Ich mein selbst ich lerne nie aus. Gab es denn an meinen anderen Aussagen noch was zu bemängeln?
Ehrlich gesagt teile ich nicht deine Meinung. Nicht jeder Stil ist gut, selbst wenn man konsequent ist. Es gibt handfeste Gründe gegen (systems) hungarian Notation. Über diese muss man auch sprechen dürfen und ein Programm in diesem Stil bemängeln, da es einfach besser ginge. Sogar ohne viel Aufwand. Daher spricht eben nichts dafür und wenn man diese Argumente gehört hat und trotzdem weiter systems hungarian in einer Sprache wie C++ (in C ist es noch einigermaßen rechtfertigbar) benutzt, dann ist man eben einfach ein sturer Bock.
Auch wäre ich vorsichtig mit der Verteidigung von goto. 99.9999% aller Einsätze von goto durch Anfänger sind Müll. Totaler Müll. Müll der sie daran hindert, jemals besser zu werden. Daher ist es durchaus eine gute Idee, ihnen dogmatisch das goto zu verbieten. Später, wenn sie dann soweit sind, die Prinzipien die ihnen beigebracht wurden selbstständig(!) zu hinterfragen, dann werden sie auch erkennen, dass dies kein absolutes Verbot sein braucht. Und sie werden auch feststellen, dass sinnvolle gotos extrem selten sind und sie nicht viel verpasst haben. Selbst sinnvolle gotos sind meistens nicht so viel besser als die Alternativen mit den "sauberen" Kontrollstrukturen, wohingegen ein schlecht gesetztes goto wesentlich schlechter ist als die saubere Alternative.
-
Marc++us schrieb:
@Programmierfachprofi:
Man sieht deutlich, daß Deine Variante semantisch gleich ist, durch die notwendige Duplizierung von Code aber fehleranfälliger und damit auch schlechter wartbar. Dein Beispiel war eigentlich wunderbar geeignet, um die Vorteile eines goto zu beleuchten.
Deine Sprungmarken erhöhen ebenfalls die Zeilenanzahl und somit die Fehleranfälligkeit.
Außerdem kann ein Compiler so ein Konstrukt nicht mehr so gut optimieren, das dürfte das Totschlagargument für GOTO sein.cooky451 schrieb:
da fehlt ein closeFile().
Bitteschön:
int foo(....){ if (!openDataFile()) return 1; if (!getDataFromFile()) { closeFile(); return 1; /* oder 2, wer damit was machen will (z.B. Ausgabe der Fehlermeldung oder weiterführende delegierte Fehlerbehandlung etc. */ ) if (!allocateSomeResources) { freeResources(); closeFile(); return 1; // siehe oben } // Do more work here.... freeResources(); // free resources closeFile(); // close file return 0; // quit! }