Doppelschleife verlassen - Sprungmarke vermeiden
-
@Euler:
Ja in der Theorie gibt es Fälle wo "goto" auch in C++ angebracht scheint (C ist da sowieso was anderes, da keine Destruktoren etc., da braucht man öfters goto).
In der Praxis ... wie schon gesagt ... hatte ich sowas in den letzten Jahren nicht mehr.Und ich würde sagen dass ich ohne "goto" keinen komplizierteren Code schreibt als mit. Ganz im Gegenteil: ohne "goto" wird der Code meist sauberer. Natürlich nur wenn man es richtig macht, und nicht so seltsame Dinge macht wie DarthZiu vorgeschlagen hat.
@DarthZiu:
Solche unnötigen Variablen machen ein Programm erst recht schwer zu lesen, schwer nachzuvollziehen wie es funktioniert bzw. zu checken ob alle möglichen Fälle abgedeckt sind.@ALL:
Interessant wäre ein konkretes Beispiel wo goto angebracht scheint um eine innere Schleife zu verlassen. Dann könnte jeder "seine" Version dieses Codes posten (mit goto, mit Hilfsvariablen, mit return), dann hätte man etwas was man wirklich vergleichen kann.
-
[quote="noobLolo"]
von hinten durch die brust ins auge...
abgesehen davon, das durch die 2 flags dein code langsamer wird macht es das doch sicher nicht einfacher oder?
[/qoute]Du willst mir aber nicht erzählen, dass das bei einem modernen Prozessor einen spürbaren Unterschied gibt? Wenn wir hier von Arrays > 100.000 Elementen sprechen würden, oder von einem langsamen Prozessor, könnte ich deinen Einwand verstehen.
noobLolo schrieb:
ein programm das nicht spring ist kein programm, was macht ein "return" "break" "continue" "if" "switch" "for" "while" oder eine einfacher functionsaufruf? alle springen nur das hübsche "goto" darf nicht mit machen, also ich find das bischen übertrieben...
Tja, dann solltest du vielleicht ein wenig Erfahrung in der Wirtschaft sammeln. In meinem derzeitigen Job arbeite ich mit mehreren hundert Entwicklern an einem System, dass mehr als 7 Mio Zeilen Code hat. Wenn hier jeder anfangen würde wilde goto's einzubauen, dann würde man bald gar nicht mehr durchblicken.
Es ist ja mit dem einen goto nicht getan, weil du nach der Doppelschleife entweder über den goto-Block mit einem weiteren goto springen musst, oder halt zwei return-Zweige brauchst.
In den SW-Firmen, in denen ich bisher gearbeitet habe, sind aus gutem Grund goto's und continue's im Coding Guide verboten. Und break ist nur in switch-case-Strukturen erlaubt. Warum denkt ihr denn, dass diese Sprünge bei Prof's verpöhnt sind? Weil die wissen, was aus solchem Wildwuchs in der Realität wird.
Sicher gibt es Stellen, wo man aufgrund der Performance solche Konstrukte braucht, aber man sollte sie selten und bewusst und gut kommentiert einsetzen.
Beim Thema C auf µC ist das natürlich ne andere Geschichte.
-
DarthZiu schrieb:
Du willst mir aber nicht erzählen, dass das bei einem modernen Prozessor einen spürbaren Unterschied gibt? Wenn wir hier von Arrays > 100.000 Elementen sprechen würden, oder von einem langsamen Prozessor, könnte ich deinen Einwand verstehen.
ach das summiert sich eben auf, und es muß nicht unbedingt schnell sondern sauber sein.
DarthZiu schrieb:
Tja, dann solltest du vielleicht ein wenig Erfahrung in der Wirtschaft sammeln. In meinem derzeitigen Job arbeite ich mit mehreren hundert Entwicklern an einem System, dass mehr als 7 Mio Zeilen Code hat. Wenn hier jeder anfangen würde wilde goto's einzubauen, dann würde man bald gar nicht mehr durchblicken.
ja da hast du mir evtl. etwas voraus, ich habs bisher mit meinen kentnissen in noch keine firma geschafft und tippsel daheim so vor mich hin, daher ist auch der einzige (meißte) code den ich mir anschauen kann opensource
der linux kern ist voll von gotos, und weil mich rüdiger gestern in einer diskussion auf die strstr() implementation der glibc gestoßen hat post ich mal nen auszug
/* * My personal strstr() implementation that beats most other algorithms. * Until someone tells me otherwise, I assume that this is the * fastest implementation of strstr() in C. * I deliberately chose not to comment it. You should have at least * as much fun trying to understand it, as I had to write it :-). * * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ #if HAVE_CONFIG_H # include <config.h> #endif #if defined _LIBC || defined HAVE_STRING_H # include <string.h> #endif typedef unsigned chartype; #undef strstr char * strstr (phaystack, pneedle) const char *phaystack; const char *pneedle; { const unsigned char *haystack, *needle; chartype b; const unsigned char *rneedle; haystack = (const unsigned char *) phaystack; if ((b = *(needle = (const unsigned char *) pneedle))) { chartype c; haystack--; /* possible ANSI violation */ { chartype a; do if (!(a = *++haystack)) goto ret0; while (a != b); } if (!(c = *++needle)) goto foundneedle; ++needle; goto jin; for (;;) { { chartype a; if (0) jin:{ if ((a = *++haystack) == c) goto crest; } else a = *++haystack; do { for (; a != b; a = *++haystack) { if (!a) goto ret0; if ((a = *++haystack) == b) break; if (!a) goto ret0; } } while ((a = *++haystack) != c); } crest: { chartype a; { const unsigned char *rhaystack; if (*(rhaystack = haystack-- + 1) == (a = *(rneedle = needle))) do { if (!a) goto foundneedle; if (*++rhaystack != (a = *++needle)) break; if (!a) goto foundneedle; } while (*++rhaystack == (a = *++needle)); needle = rneedle; /* took the register-poor aproach */ } if (!a) break; } } } foundneedle: return (char *) haystack; ret0: return 0; } libc_hidden_builtin_def (strstr)und weils gar so schön ist noch ein kleiner auszug aus dem linux kernel
int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_path.dentry->d_inode; int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; res = mutex_lock_killable(&inode->i_mutex); if (res) goto out; res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); file_accessed(file); } mutex_unlock(&inode->i_mutex); out: return res; }ich denke das ist auch große software und da arbeiten ne ganze menge leute > 1000 dran die nochmal ein ganzes stückchen klüger sind als ich, ich hab zwar keinen plan warum die das so machen geschweige denn das ich verstehe was die da treiben, aber die werden schon wissen was sie machen...
sonst würd das zeug ja nicht in dem umfang verwendet

lg lolo
-
Der Linux-Kernel und die Implementierung der Standard-Librarys fallen definitiv unter:
DarthZiu schrieb:
Sicher gibt es Stellen, wo man aufgrund der Performance solche Konstrukte braucht, aber man sollte sie selten und bewusst und gut kommentiert einsetzen.
-
@DarthZiu:
du kannst mir doch nicht erzählen das die paar gotos aus der 'vfs_readdir()' unübersichtlich sind?die strstr() wird so wie ich das sehe für längere zeit nicht verändert, und wenn doch dann entweder von dem der sie gebaut hat, oder komplett neu gemacht.
gotos sind doch eh nur innerhalb der function gültig, da die eh nicht so lang sein sollen ist das doch null problemo?
naja denke das wird eine endlose diskussion die kein ende findet weil keiner nachgibt, mir ist es gleich wenn einer ein goto verwendet, da find ich jede globale variable schlimmer aber naja ich kann auch deine meinung verstehen, und natürlich geht auch 99% ohne goto, aber es ist nun mal ein teil der sprache und das zu verbieten ist als würd ich dir den kleinen finger abschneiden, denn den verwendest eh in 99% der fälle nicht^^
lg lolo
-
noobLolo schrieb:
* Until someone tells me otherwise, I assume that this is the * fastest implementation of strstr() in C.ich denke das ist auch große software und da arbeiten ne ganze menge leute > 1000 dran die nochmal ein ganzes stückchen klüger sind als ich, ich hab zwar keinen plan warum die das so machen geschweige denn das ich verstehe was die da treiben, aber die werden schon wissen was sie machen...
Für meinen Geschmack zuviele Annahmen...
-
Warum streitet ihr euch eigentlich in einem C++-Forum über alte Denkschulen der Fehlerbehandlung in C? Es steht doch außer Frage, dass kein C++-Programmierer mit mehr als zwei Wochen Erfahrung (sprich, der schonmal was von Exceptions gehört hat) vfs_readdir so aufbauen würde.
Und zu dieser strstr-Implementation verliere ich besser kein Wort.
-
noobLolo schrieb:
und weils gar so schön ist noch ein kleiner auszug aus dem linux kernel
int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_path.dentry->d_inode; int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; res = mutex_lock_killable(&inode->i_mutex); if (res) goto out; res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); file_accessed(file); } mutex_unlock(&inode->i_mutex); out: return res; }Was soll n der Quatsch? Ein goto, das zu nem return spring. Wieso nicht gleich return res?
-
Weil der Compiler Funktionen mit nur einem return Wert besser optimieren kann
-
dfgdhdh schrieb:
Weil der Compiler Funktionen mit nur einem return Wert besser optimieren kann
Ich bezweifle, dass das ein Unterschied für dem Compiler macht, ob nun eine Sprunganweisung in Form eines goto da steht oder in Form von einem direktem return.
-
dfgdhdh schrieb:
Weil der Compiler Funktionen mit nur einem return Wert besser optimieren kann
Und
gotokann er natürlich so unglaublich gut optimieren
Grüssli
-
seldon schrieb:
Warum streitet ihr euch eigentlich in einem C++-Forum über alte Denkschulen der Fehlerbehandlung in C? Es steht doch außer Frage, dass kein C++-Programmierer mit mehr als zwei Wochen Erfahrung (sprich, der schonmal was von Exceptions gehört hat) vfs_readdir so aufbauen würde.
Und zu dieser strstr-Implementation verliere ich besser kein Wort.
ich dachte es ging unm "goto" und nicht die Fehlerbehandlung in C
lg lolo
-
seldon schrieb:
Es steht doch außer Frage, dass kein C++-Programmierer mit mehr als zwei Wochen Erfahrung (sprich, der schonmal was von Exceptions gehört hat) vfs_readdir so aufbauen würde.
Mit C++ Exceptions sollte man sehr sparsam sein.
http://msdn.microsoft.com/en-us/library/c0hwkhwe(VS.80).aspx
-
C++Fan 2010 schrieb:
seldon schrieb:
Es steht doch außer Frage, dass kein C++-Programmierer mit mehr als zwei Wochen Erfahrung (sprich, der schonmal was von Exceptions gehört hat) vfs_readdir so aufbauen würde.
Mit C++ Exceptions sollte man sehr sparsam sein.
http://msdn.microsoft.com/en-us/library/c0hwkhwe(VS.80).aspxblah
blaaaaaaaaaaaaaaaaaaaaaaaah6, setzen
-
also hustbaer, beweise belege, erfahrungen o.ä. blah bringt uns alle nicht weiter

evtl. erninnere ich mal an das thread thema "Doppelschleife verlassen - Sprungmarke vermeiden"
und nicht "C++ Exceptions"lg lolo
-
hustbaer schrieb:
blah
blaaaaaaaaaaaaaaaaaaaaaaaahDu wolltest mir was mitteilen?
-
noobLolo schrieb:
also hustbaer, beweise belege, erfahrungen o.ä. blah bringt uns alle nicht weiter

evtl. erninnere ich mal an das thread thema "Doppelschleife verlassen - Sprungmarke vermeiden"
und nicht "C++ Exceptions"Um eine Brücke zu schlagen: ich denke Freund hustbär würde die Schleife mittels einer geworfenen Exception verlassen.
Das ist sicherlich nicht im Sinne des Erfinders.
-
Es ist zwar richtig, dass Exception-Handling einen gewissen Overhead erzeugt, allerdings bezieht sich dieser bei der üblichsten Form des Exception-Handlings (table drive) vor allem auf die Größe des Kompilats. Insbesondere ist der Laufzeitoverhead für Fälle, in denen keine Exception geschmissen wird, eher gering - was für einen Fehlerbehandlungsmechanismus ja auch Sinn macht.
Dementsprechend ist exception-basierte Fehlerbehandlung keineswegs weniger performant, als nach jeder Aktion auf Fehler zu prüfen und ggf. irgendwohin zu springen, von Lesbarkeit ganz zu schweigen. Deshalb bleibe ich dabei, dass kein C++-Programmierer, der die Sprache auch nur ansatzweise versteht, vfs_readdir so aufbauen würde.
Was das Springen aus der Schleife angeht, ist eine Exception allerdings in der Regel wohl wirklich keine sinnvolle Methode. In den meisten Fällen dürfte es sinnvoller sein, die innere Schleife in eine eigene Funktion auszulagern, aber das muss im Einzelfall entschieden werden. Es mag Fälle geben, in denen goto eine vertretbare Möglichkeit ist, allerdings sind die damit verbundenen Eigenheiten (beispielsweise in Bezug auf Objektlebenszeiten) derart haarig, dass ich schon etwas Hirnschmalz darauf verwenden würde, es zu vermeiden.
-
seldon schrieb:
Dementsprechend ist exception-basierte Fehlerbehandlung keineswegs weniger performant, als nach jeder Aktion auf Fehler zu prüfen und ggf. irgendwohin zu springen, von Lesbarkeit ganz zu schweigen.
Teilweise richtig. Aber wenn Du eine differenzierte Fehlerbehandlung brauchst, musst Du auch die Exceptions auswerten und entspreched reagieren.
seldon schrieb:
Es mag Fälle geben, in denen goto eine vertretbare Möglichkeit ist, allerdings sind die damit verbundenen Eigenheiten (beispielsweise in Bezug auf Objektlebenszeiten) derart haarig, dass ich schon etwas Hirnschmalz darauf verwenden würde, es zu vermeiden.
Wann Objekte sterben ist genau definiert: beim Verlassen des Scopes. IMHO sollte Dich der Compiler darauf aufmerksam machen, wenn Du mit GOTO versuchst, dieses Prinzip auszuhebeln.
-
Kleines Experiment mit dem strstr-Teil
#include <stddef.h> typedef unsigned chartype; char* strstr (const char* phaystack, const char* pneedle) { ... Originalcode } char* strstr2(const char* phaystack, const char* pneedle) { if ( !pneedle[ 0 ] ) return (char*) phaystack; if ( !pneedle[ 1 ] ) { for (;;) { if ( !*phaystack ) return 0; if ( phaystack[ 0 ] == pneedle[ 0 ] ) break; ++phaystack; } return (char*) phaystack; } for (;;) { for (;;) { if ( !*phaystack ) return 0; if ( phaystack[ 0 ] == pneedle[ 0 ] && phaystack[ 1 ] == pneedle[ 1 ] ) break; ++phaystack; } { ptrdiff_t i = 2; for ( ;; ) { if ( !pneedle[ i + 0 ] ) return (char*) phaystack; if ( phaystack[ i + 0 ] != pneedle[ i + 0 ] ) break; ++i; } } ++phaystack; } } #include <iostream> #include <vector> #include <string> #include <ctime> #include <cstdlib> using namespace std; string randomstring(int length) { string s; for ( ; length--; ) s.push_back(rand()%256); return s; } int main() { const int x = 10000000; srand(time(0)); string haystack(randomstring(40000000)); vector<string> needles; for ( int i = 0; i < x; ++i ) needles.push_back(randomstring(rand()%32+1)); time_t start = clock(); char a = 0; for ( int i = 0; i < x; ++i ) { char* p = strstr(haystack.c_str(),needles[i].c_str()); a += p ? *p : 0; } cout << ( clock() - start ) << '\t' << a << '\n'; start = clock(); a = 0; for ( int i = 0; i < x; ++i ) { char* p = strstr2(haystack.c_str(),needles[i].c_str()); a += p ? *p : 0; } cout << ( clock() - start ) << '\t' << a << '\n'; }strstr2 benutzt den gleichen Algorithmus, aber ohne den ganzen goto-Unfug. Nicht besonders schön, aber jedenfalls ist eine gewisse Regelmäßigkeit da im Gegensatz zum Ursprungscode.
Compiliert mitg++ -O2 teststrstr.cpp -save-temps -march=core2 -mtune=pentiummit gcc 4.4.2 für Atom. Da das glibc typischerweise eher mit konservativen Einstellungen gebaut wird, sind diese Flags sinnvoll.
Ergebnis: strstr2 ist ca 10-15% langsamer als strstr.
Alle Loops in strstr2 4fach aufgerollt (Code wurde bewusst so geschrieben, dass das mit C&P trivial möglich ist, für den Compiler ist es leider zu kompliziert) - Ergebnis: strstr2 ist ca. 30-40% schneller als strstr. Interessanterweise ist der erzeugte Assemblercode trotzdem kürzer.
Fazit: Insoweit strstr schneller als andere Implementationen ist, hat das wahrscheinlich nichts mit den gotos zu tun.