Wieso ist das Programm ein Alptraum?
-
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! }
-
knivil schrieb:
Beispielsweise folgender suaberer Code ist trotzdem schwer zu lesen
Bei den Variablennamen wundert mich das auch nicht.
Daher Definieren wir:
sauberer Code = enthält gute aussagekräftige Variablennamen
-
Daher Definieren wir:
sauberer Code = enthält gute aussagekräftige VariablennamenGrins

Extremes Negativbeispiel, welches ich kennenlernte:double FestgelegteRotationKXYZPXYZ::GetWert() { return Variablen[VariablenIndizes[1]]*sin(Variablen[VariablenIndizes[2]])+Variablen[VariablenIndizes[2]]*Variablen[VariablenIndizes[0]]+cos(Variablen[VariablenIndizes[3]])+Variablen[VariablenIndizes[4]]*Variablen[VariablenIndizes[1]]*sin(Variablen[VariablenIndizes[3]]; }
-
Programmierfachprofi schrieb:
Deine Sprungmarken erhöhen ebenfalls die Zeilenanzahl und somit die Fehleranfälligkeit.
Zeilenanzahl ist ungleich Komplexität, damit ohne Zusammenhang zur Fehleranfälligkeit.
Aber die Notwendigkeit Code zu duplizieren und bei Änderungen an jeder Duplikationsstelle mitzuändern ist extrem fehleranfällig.
Programmierfachprofi schrieb:
Außerdem kann ein Compiler so ein Konstrukt nicht mehr so gut optimieren, das dürfte das Totschlagargument für GOTO sein.
Hm? Woraus leitest Du das ab? Da diese Form der goto-Fehlerbehandlung gerade in Unix-Kernels verwendet wird UND deren Übersetzung ein Standardbenchmark für jeden Compiler ist, haben die mit hoher Wahrscheinlichkeit darüber nachgedacht.
Für den Compiler ist das goto ja nur ein weiterer Pfad... es gibt einmal den regulären Pfad, und dann welche mit goto. Der Compiler baut sich darauf seinen Ablaufgraph auf und optimiert entlang des Ausführungspfads. Es zwar richtig, daß man von goto aus jedem Kontext rausspringen kann, aber wenn das Programm übersetzt wird sind alle diese Exit-Punkte dem Compiler bekannt.
-
cooky451 schrieb:
seldon schrieb:
als der gleiche Code
Konsequent weitergedacht bedeutet das, dass keine Funktion mehr als eine Ressource reservieren darf.
Das hab ich damit nicht gemeint, sondern, dass eine Funktion, die sich mit Ressourcenallokation beschäftigt, (fast)* nur mit Ressourcenallokation beschäftigen sollte, und nicht mit der Verwendung selbiger. Das kann dann ruhig auch ne static-Funktion direkt über der verwendenden Funktion sein.
Damit hat man den fummeligen Teil an einer Stelle, kommt aus der Ressourcenanforderung in einem von zwei Zuständen wieder raus ("alles geklappt, Ressourcen angefordert" oder "ist was schief gegangen, alles schon wieder aufgeräumt") und ist einen Großteil der Komplexität los.
Wenn man in einer Funktion mehrere Ressourcen braucht, wird man, wie gesagt, die Kaskade nicht unbedingt los, aber man hat sie isoliert -- und eine Funktion, die sich nur mit der Anforderung von so Größenordnung drei Kontextobjekten beschäftigt, kriegt man mit oder ohne goto lesbar gebacken (ich mach das dann lieber ohne). Wenn man in einer Funktion deutlich mehr einzelne Ressourcenanforderungen hat, hat man aus meiner Sicht noch ganz andere Strukturprobleme -- das schlägt dann in die selbe Bresche wie die Funktionen-sollten-nicht-länger-als-50-Zeilen-lang-sein-Faustregel.
* nach Sachlage im Einzelfall zu beurteilen
-
Marc++us schrieb:
Programmierfachprofi schrieb:
Deine Sprungmarken erhöhen ebenfalls die Zeilenanzahl und somit die Fehleranfälligkeit.
Zeilenanzahl ist ungleich Komplexität, damit ohne Zusammenhang zur Fehleranfälligkeit.
Aber die Notwendigkeit Code zu duplizieren und bei Änderungen an jeder Duplikationsstelle mitzuändern ist extrem fehleranfällig.
Deswegen lagert man das in Funktionen aus, benutzt inline-ersetzung oder Funktionsmakros.
Programmierfachprofi schrieb:
Außerdem kann ein Compiler so ein Konstrukt nicht mehr so gut optimieren, das dürfte das Totschlagargument für GOTO sein.
Hm? Woraus leitest Du das ab? Da diese Form der goto-Fehlerbehandlung gerade in Unix-Kernels verwendet wird UND deren Übersetzung ein Standardbenchmark für jeden Compiler ist, haben die mit hoher Wahrscheinlichkeit darüber nachgedacht.
Weil ein Compiler nicht wirklich weiß, wo im Spagetthicode die Sprungmarke ist.
D.h. er findet sie zwar schon, aber eine Kenntnis über eine strukturierte Verschachtelung fehlt ihm völlig und mit diesem fehlenden Wissen kann er nur schlecht optimieren.Das es Standardbenchmakrs für Compiler gibt ist klar, immerhin wird das für den Compiler sehr schwer, da noch etwas zu optimieren.
Die Standardbenchmarks zeigen also, dass gotos zu optimierproblemen führen.Wenn ich meinen Compiler stattdessen mit einem ordentlich strukturierten Code füttere, so wie man es früher mit Pascal gelernt hat, dann weiß der wenigstens wo er optimieren kann und das macht er dann sehr gut.
Für den Compiler ist das goto ja nur ein weiterer Pfad... es gibt einmal den regulären Pfad, und dann welche mit goto. Der Compiler baut sich darauf seinen Ablaufgraph auf und optimiert entlang des Ausführungspfads.
Dem Ablaufgraph fehlt allerdings die Schachtelung, wenn der Programmierer sich mit den Gotos dumm angestellt hat.
-
Zeilenanzahl ist ungleich Komplexität, damit ohne Zusammenhang zur Fehleranfälligkeit.
Ich habe mal gehoert, dass Zeilenanzahl direkt proportional mit der Anzahl der Bugs korreliert. Das soll unabhaengig von Sprache, Programmierstil etc. Also etwa 100 Zeilen ein Bug. Siehe http://de.wikipedia.org/wiki/Fehlerquotient wobei ich in den Quellen nicht nachgelesen habe.
-
Auch wenn goto grundsaetzlich gemiden werden sollte gibt es durchaus ein paar Ausnahmen, z.B.:
1. Um aus einer verschachtelten Schleife rauszuspringen (muss man wohl kaum erklaren unter welchen Umstaenden das wuenschenswert ist.
2. Nested Cleanup Code (wird zum Beispiel auch im Linux Kernel so gemacht).@ Programmierfachprofi
Warum sollte ein Compiler strukturierten Code besser optimieren koennen als Code mit goto? Kannst du ein konkretes Beispiel geben bzw. etwas ausfuehrlicher sein?*Edit
Auf http://www.systems.ethz.ch/courses/fall2012/SPCA in der 10. Vorlesung "Advanced C" gibts einen Abschnitt "goto" und dort gibts Beispiele zu Punkt 1 und 2.
-
Ich finde nicht, dass der Linuxkernel ein gutes Beispiel ist. Die meisten werden doch eher Anwendungssoftware schreiben, d.h. ist das Argument fehl am Platz.
1. Um aus einer verschachtelten Schleife rauszuspringen
Gar nicht erst verschachtelte Schleifen schreiben.
*Edit
Auf http://www.systems.ethz.ch/courses/fall2012/SPCA in der 10. Vorlesung "Advanced C" gibts einen Abschnitt "goto" und dort gibts Beispiele zu Punkt 1 und 2.Und fuer Punkt 1.) wurde die Alternative gleich genannt und Punkt 2.) bezieht sich auf den Kernel.
-
icarus2 schrieb:
@ Programmierfachprofi
Warum sollte ein Compiler strukturierten Code besser optimieren koennen als Code mit goto? Kannst du ein konkretes Beispiel geben bzw. etwas ausfuehrlicher sein?Das Problem ist nicht goto per se, sondern ein irreduzibler Kontrollflussgraph. Damit funktionieren einige Optimierungsalgorithmen nicht. Mein Wissen reicht auf dem Gebiet aber nicht über Halbwissen hinaus, deshalb soll das an der Stelle genügen. Ein google-Stichwort hab ich ja gegeben.
-
Bashar schrieb:
sondern ein irreduzibler Kontrollflussgraph.
... aber der liegt in den hier gezeigten Beispielen doch kaum vor. Das ist ungefähr vergleichbar wie ein Switch-case.
-
knivil schrieb:
Ich finde nicht, dass der Linuxkernel ein gutes Beispiel ist. Die meisten werden doch eher Anwendungssoftware schreiben, d.h. ist das Argument fehl am Platz.
Mein Punkt war nicht, dass jeder Verwendung dafuer findet sondern bloss, dass eine kategorische Ablehnung gegenueber goto nicht berechtigt ist.
knivil schrieb:
Gar nicht erst verschachtelte Schleifen schreiben.
Dass man in stinknormaler Anwendungssoftware keine verschachtelten Schleifen braucht kann ich noch glauben (ich weiss es nicht, denn ich habe leider noch nie als Softwareentwickler gearbeitet). Aber bei gewissen Algorithmen frage ich mich schon wie man da um verschachtelte Schleifen rumkommt.
Mir ist klar, dass die meisten Programmierer damit nie konfrontiert werden. Mein Punkt war bloss, dass in gewissen Bereichen goto durchaus Verwendung findet. Auch wenn die meisten das nie brauchen schadet es nicht das zu wissen.
@ Bashar:
Thx fuer das Stickwort. Werde mal danach googeln.
-
knivil schrieb:
Ich habe mal gehoert, dass Zeilenanzahl direkt proportional mit der Anzahl der Bugs korreliert.
Absolut gesehen ja.
Ich habe es hier auf das Beispiel von ihm bezogen, ich glaube ich habe eine Zeile mehr, weil ich für goto + Label eine Zeile mehr brauche als seine Lösung.
-
Mein Punkt war bloss, dass in gewissen Bereichen goto durchaus Verwendung findet.
Das zweifelt niemand an. Dennoch ist es in den meisten Faellen eine schlechte Idee.