Forum: Compiler & IDEs GOTO oder While-Schleife? Was ist Besser oder einfacher?


von Axel L. (lemmi4711)


Lesenswert?

Mal was ganz rhetorisches...

Was meint Ihr?

Die Frage kam mir in den Sinn, da ein sogenannter Gast-Poster meinen 
Sourcecode "bemängelte"...

von Johannes M. (johnny-m)


Lesenswert?

goto hat in einem C-Programm normalerweise nichts verloren, da es 
den eigentlichen Vorteil der strukturierten Programmierung aushebelt! Es 
gibt einige ganz wenige Fälle, in denen goto Sinn machen kann 
(deshalb ist es Bestandteil der Sprache), und zwar z.B. zur 
Fehlerbehandlung oder zum Verlassen tief verschachtelter Strukturen, 
wenn es z.B. mit break nicht machbar ist. Wenn die Möglichkeit 
besteht, goto zu vermeiden, dann tu es.

Die Erfinder von C schreiben selbst:
"C provides the infinitely-abusable goto statement, and labels to 
branch to. Formally, the goto is never necessary , and in practice 
it is almost always easy to write code without it. [...] Nevertheless, 
there are a few situations where goto may find a place. The most 
common is to abandon processing in some deeply nested structure, such as 
breaking out of two or more loops at once. [...]"
(Kernighan & Ritchie, The C Programming Language)

von Wolfgang Mües (Gast)


Lesenswert?

"goto" wird (in C) in der Regel benutzt, wenn es entweder einen 
zentralen Aussprungpunkt aus der Funktion gibt (um "finally" von C++ zu 
ersetzen), oder wenn es aus einer komplexen Schleife wieder an den 
Anfang gehen soll.

Vor einigen Jahr(zehnten) was es üblich, GOTO als Konstrukt vollständig 
zu verbannen (die sogenannte "reine Lehre"). Wohin solche 
prinzipiell-theoretischen Dogmen führen, ist ja reichlich bekannt.

Du solltest Dich bei der Architektur Deines Programms davon leiten 
lassen, dass die einzelnen Programmteile übersichtlich und verständlich 
sind, und das die einzelnen Themenbereiche sinnvoll voneinander getrennt 
sind.

Ein Programm ist perfekt, wenn man nichts mehr weglassen kann.

von Gast (Gast)


Lesenswert?

Ein Programm ist dann perfekt, wenn es tut was es soll und der 
Programmcode übersichtlich und nachvollziehbar ist.
Dazu gehört auch eine gewisse Anzahl an Kommentaren, wo man seine 
Überlegungen niederlegt, um das ganze auch dann noch zu verstehen, wenn 
man längst auf einer anderen Baustelle denkt.
Dazu gehört eben auch, dass man nicht wild im Code hin und her springt. 
Eben versucht etwa Goto mit Bedacht einzusetzen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Literatur zum Thema: Donald E. Knuth, "Literate Programming".
Kapitel 2 dort heißt "Structured Programming with go to statements".
Darin setzt er sich mit der "reinen Lehre" und allem für und wider
zu go to statements auseinander.

von Stefan Salewski (Gast)


Lesenswert?

Wikipedia hat auch etwas zum Thema, aber das hatte ich dem 
Thread-Starter ja schon geschrieben.

http://de.wikipedia.org/wiki/Sprunganweisung

von Bernhard R. (barnyhh)


Lesenswert?

Hier gibt es meiner Meinung nach zwei Kriterien:
1. Was ist für einen menschlichen Leser verständlicher?
2. Was passiert im Falle einer Programm-Änderung?

zu 1:
Saubere Strukturierung per Code-Einrückung und Klammerung (mit 
geschweiften Klammern) zeigt dem Leser sofort und machvollziehbar, wo es 
lang geht. Das ist mit Sprunglabeln nicht unbedingt erreichbar.
Das GOTO-Konstrukt fällt aus dieser Struktur heraus - es zerstört sie.

zu 2:
Wenn die Struktur des Programms sich ändert, dann vergißt man gerne die 
GOTO's und "nagelt sich so ganz gewaltige Eier auf die Schiene"! Ich 
spreche hier aus eigener harter Erfahrung.

Noch eine persönliche Anmerkung:
Wenn ich in den letzten 20 Jahren in meiner beruflichen Umgebung 
(professionelle Software-Entwicklung) GOTO-behafteten Code gesehen habe, 
dann war diesem Code genau anzusehen, daß der Autor seine krausen 
Gehirnwindungen vor Betätigen der Tastatur nicht geordnet hatte.
Dementsprechend hoch war dann auch der jeweilige Aufwand für Debugging 
und Wartung.

Bernhard

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernhard R. wrote:

> Saubere Strukturierung per Code-Einrückung und Klammerung (mit
> geschweiften Klammern) zeigt dem Leser sofort und machvollziehbar, wo es
> lang geht.

Du hast noch keinen Code mit 20 Einrückungsebenen gesehen.  Auf sowas
kommt man nämlich zuweilen, wenn goto strikt verboten ist, und man
daher immer mehr if-Anweisungen schachteln muss, um den Rücksprung
im Falle eines früh erkannten Fehlers zu umgehen.

Ansonsten kann ich dir die Lektüre von Knuth empfehlen.

von Paul H. (powl)


Lesenswert?

Jörg Wunsch wrote:
> Bernhard R. wrote:
>
>> Saubere Strukturierung per Code-Einrückung und Klammerung (mit
>> geschweiften Klammern) zeigt dem Leser sofort und machvollziehbar, wo es
>> lang geht.
>
> Du hast noch keinen Code mit 20 Einrückungsebenen gesehen.  Auf sowas
> kommt man nämlich zuweilen, wenn goto strikt verboten ist, und man
> daher immer mehr if-Anweisungen schachteln muss, um den Rücksprung
> im Falle eines früh erkannten Fehlers zu umgehen.
>
> Ansonsten kann ich dir die Lektüre von Knuth empfehlen.

Dem kann ich mich nur anschließen. In PHP und JavaScript vermisse ich 
die Goto-Anweisung deshalb desöfteren weil man sie bei der 
Fehlerbehandlung wirklich brauchen kann. Mit If usw. kann man hier 
wirklich nur umwege gehen. Und die Verschachtelungen machen das ganze 
nicht übersichtlicher.

lg PoWl

von yalu (Gast)


Lesenswert?

> In PHP und JavaScript vermisse ich die Goto-Anweisung deshalb
> desöfteren weil man sie bei der Fehlerbehandlung wirklich brauchen
> kann.

Dafür gibt es Excpetions (try-catch). Die ersetzen insbesondere auch
den longjmp von C. Für Sprünge innerhalb einer Funktion sind sie
vielleicht etwas überdimensioniert, was aber bei Skriptsprachen keine
so große Rolle spielt.

von Paul H. (powl)


Lesenswert?

stimmt.. aber entsprechen exceptions nicht eigentlich auch einer 
if-struktur + fehler-variable?

wenn nicht, muss ich mich damit nochmal beschäftigen
lg PoWl

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nee, exceptions sind eher ein OO-Konzept, daher auch entsprechend
schwergewichtig (kosten also einiges an Codegröße).  Die Auflösung,
wohin die Exception tatsächlich geworfen wird, erfolgt gewissermaßen
als "late binding" erst zur Laufzeit.

Weiß nicht, inwiefern der Compiler, wenn die Auflösung zur Compilezeit
schon feststeht, dass dann ggf. optimiert.

Disclaimer: das ist das, wie ich Exceptions verstanden habe.  Ich habe
sie noch nicht viel benutzt, insbesondere nicht in Sprachen wie C++
(in Python schon eher mal, aber da ist ja eh' alles late binding).

von Andreas K. (a-k)


Lesenswert?

Paul Hamacher wrote:

> stimmt.. aber entsprechen exceptions nicht eigentlich auch einer
> if-struktur + fehler-variable?

Wie ein Compiler das realisiert bleibt ihm überlassen. Da exceptions 
aber oft funktionsübergreifend arbeiten können, sind Fehlervariablen 
dabei eher unwahrscheinlich. Das erinnert, vereinfacht ausgedrückt, eher 
an setjmp/longjmp, plus automatischer Freigabe diverser dabei über Bord 
gehender Variablen abgebrochener Funktionen über Analyse des 
Stack-Inhalts. Ist aber ein durchaus komplexes Thema.

von Peter D. (peda)


Lesenswert?

Also ich benutze "gotos" alle Nase lang. Wenn man es sich mal genauer 
anguckt, ist nämlich jedes switch/case eine astreine Goto-Anweisung. 
Durch den ":" ist das auch deutlich ersichtlich und man kann ja auch 
beliebig in anderen Code hineinspringen.

Sehr störend ist allerdings das "break" als Beenden eine Case-Anweisung 
und zum Verlassen einer Schleife. Da muß man oft viele if-Anweisungen 
schachteln, statt einer switch-Anweisung.

Wenn ich von den vielen geschachtelten "ifs" die Nase voll habe, nehme 
ich auch mal ne Unterfunktion und kann dann mit "return" in nem "case" 
die Schleife verlassen.


Peter

von Bobby (Gast)


Lesenswert?

@Peter Dannegger

> Wenn ich von den vielen geschachtelten "ifs" die Nase voll habe, nehme
> ich auch mal ne Unterfunktion und kann dann mit "return" in nem "case"
> die Schleife verlassen.

Du sprichst mir aus der Seele...

von Rolf Magnus (Gast)


Lesenswert?

>> In PHP und JavaScript vermisse ich die Goto-Anweisung deshalb
>> desöfteren weil man sie bei der Fehlerbehandlung wirklich brauchen
>> kann.
>
> Dafür gibt es Excpetions (try-catch).

Die sind, was die Codestrukturierung angeht, ähnlich wie goto, nur 
schlimmer, da sie nicht nur innerhalb einer Funktion, sondern eben auch 
noch über Aufrufgrenzen hinweg springen.

> Das erinnert, vereinfacht ausgedrückt, eher an setjmp/longjmp,

GCC kann optional so gebaut werden, daß er setjmp/longjmp verwendet, um 
Exceptions zu implementieren.

> plus automatischer Freigabe diverser dabei über Bord
> gehender Variablen abgebrochener Funktionen über Analyse des
> Stack-Inhalts.

Das ist ein großer Vorteil von C++ gegenüber vielen anderen 
objektorientierten Sprache, die die Ressourcen nicht automatisch 
freigeben. Da muß man das immer explizit von Hand tun.

von I_ H. (i_h)


Lesenswert?

Zitat aus dem Wikiartikel oben:
"In der Praxis hat sich jedoch gezeigt, dass der Verzicht auf Goto zwar 
möglich ist, jedoch in einigen Fällen zu sehr aufwändigen Konstrukten 
führt. Von einigen Entwicklern wurde auf der Linux Kernel Mailing List 
die häufige Verwendung von Goto im Quellcode von Linux diskutiert. Linus 
Torvalds sagte dabei, dass die Verwendung von Goto die Lesbarkeit des 
Quellcodes in vielen Fällen sogar deutlich erhöhen könne."
Am Linux Kernel schreiben genug Leute mit, als dass die Meinung als 
fundiert gelten kann.

Ist doch mit allen Sachen so, man kann auch die Operatoren in C/C++ so 
einsetzen, dass man genau garnix mehr versteht. Leute die die Disziplin 
haben um damit umgehen zu können benutzen es da wo's Sinn macht... alle 
anderen bleiben bei Java ;-)

von Axel L. (lemmi4711)


Lesenswert?

Hallo, Leute.

Ich sehe, die Meinungen gehen weit auseinander.

Nun, ich vertrete die Meinung, daß goto ruhig verwendet werden kann, da 
es, insbesondere für Anfänger, einfacher ist Codes zu erstellen.

Die Verwendung von while-Schleifen, wird sicherlich in 
Interrupt-Routinen, wo Anfänger nicht rumrühren, seine Vorteile haben.

Grüße, Axel.

von Karl H. (kbuchegg)


Lesenswert?

Axel Lemke wrote:
> Hallo, Leute.
>
> Ich sehe, die Meinungen gehen weit auseinander.
>
> Nun, ich vertrete die Meinung, daß goto ruhig verwendet werden kann, da
> es, insbesondere für Anfänger, einfacher ist Codes zu erstellen.

Seh ich gerade umgekehrt.
Gerade Anfänger sollten die Finger von goto lassen.

goto hat durchaus seine Berechtigung, will aber weise und mit
Umsicht eingesetzt werden. Anfängern fehlt hier ganz einfach
die Erfahrung dazu. Dementsprechend sehen dann auch die Programme
aus und sind auch entsprechend fehleranfällig.

von Rolf Magnus (Gast)


Lesenswert?

> Nun, ich vertrete die Meinung, daß goto ruhig verwendet werden kann, da
> es, insbesondere für Anfänger, einfacher ist Codes zu erstellen.

Naja, insbesondere Anfänger wissen aber noch nicht, wo sie es einsetzen 
sollten und wo nicht.

> Die Verwendung von while-Schleifen, wird sicherlich in
>Interrupt-Routinen, wo Anfänger nicht rumrühren, seine Vorteile haben.

Hm?

von I_ H. (i_h)


Lesenswert?

Seh ich auch so, das schwierigste wenn man mit Programmieren anfängt 
ist, ein sauberes Programm zu schreiben. Irgendwie zusammenfrickeln kann 
jeder, aber wehe dann funktioniert etwas nicht, oder der Code soll um 
irgendwas erweitert werden... oder jemand anders soll sich 3 Jahre 
später in den Code reinarbeiten.

GOTO erfordert sehr viel Disziplin, C und C++ allgemein erfordern viel 
Disziplin.

von Benedikt K. (benedikt)


Lesenswert?

Ich schätze mal, ich verwende goto in etwa <1% meiner Programme, ganz 
einfach deshalb weil es einfach nahezu fast immer unnötig ist. Das 
einzige Programm das mir jetzt spontan einfällt ist eines mit mehreren 
verschachtelten Schleifen, bei denen manchmal zu einem bestimmten Punkt 
weiter vorne gesprungen wird, um ein Display wieder komplett zu 
refreshen. Vermutlich wäre es auch ohne goto gegangen, aber dann hätte 
ich mehrere Schleifen abbrechen müssen usw, also auch ziemlich 
komplizierte Sachen machen müssen.

Für Anfänger gilt eindeutig: Finger weg von goto !

von G. L. (sprintersb)


Lesenswert?

Axel Lemke wrote:
> GOTO oder While-Schleife?

Boah, es wagt jemand und öffnet die Büchse der Pandora!

Dabei kennt gelbst Java das Schlüsselwort "goto" -- allerdings nur, um 
mit einer Fehlermeldung à la "javac: goto ist böööse" das Weiterarbeiten 
zu verweigern...

Die goto-Welt spaltet sich also in Dogmatiker und Nostalgiker, deren 
BASIC-Sprachfehler bis dato noch kein Logopäde zu heilen in der Lage 
war.

Wie dem auch sei, die wahre Schönheit von goto offenbart erst GNU-C, 
welches ihm durch indirekte Sprünge vollkommene Freiheit schenkt. Zu 
bestaunen in folgendem Beispiel-Leckerli.
1
void foo (void ** label)
2
{
3
    goto *label;
4
}

avr-gcc bastelt daraus folgerichtig:
1
foo:
2
  push r24   ;  11  indirect_jump/2  [length = 3]
3
  push r25
4
  ret

In einem einzigen Projekt realisiere ich so eine Menue-Struktur, für die 
sich switch/case (wegen beschränkter Resourcen und unweigerlicher 
Expandierung des Arguments) als zu breit erwies.

bon appétit

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

>
1
> void foo (void ** label)
2
> {
3
>     goto *label;
4
> }
5
>

Hmm. Das ließe sich mit einem Funktionspointer sicherlich kaum weniger 
"elegant" abbilden:
1
void foo(void (label)(void))
2
{
3
  label();
4
}

Gut, der Compiler wird hier noch den Stack für den Funktionsaufruf 
vorbereiten, die verwendeten Funktionspointer müssen anders aussehen als 
die (auf welche Weise eigentlich gewonnenen?) Argumente der ersten 
Variante,
aber im Endeffekt sehe ich keinen sehr großen Unterschied.

von Peter D. (peda)


Lesenswert?

Georg-johann L. wrote:

>
1
> void foo (void ** label)
2
> {
3
>     goto *label;
4
> }
5
>

O.k. diese Funktion wird compiliert, aber wie verwendet man sie?

Es ist mir nicht gelungen ihr Labels zu übergeben.
Also eine völlig nutzlose Funktion.


Peter

von G. L. (sprintersb)


Lesenswert?

Rufus t. Firefly wrote:
>>
1
>> void foo (void ** label)
2
>> {
3
>>     goto *label;
4
>> }
5
>>
>
> [...] Funktionspointer müssen anders aussehen als
> die (auf welche Weise eigentlich gewonnenen?) Argumente der ersten
> Variante,
> aber im Endeffekt sehe ich keinen sehr großen Unterschied.

Function calls haben wie gesagt Nebeneffekte und sind nicht vergleichbar 
mit goto. Auch sind diese Sprünge nicht zu anderen Funktionen hin 
sinnvoll, da man dazu einen Kontext herstellen/restaurieren müsste 
(longjump).

Hier handelt es sich weniger um C-Code als um mehr oder weniger 
portierbaren Assembler, finde ich.
1
extern unsigned int getI (void);
2
3
void foo (void)
4
{
5
    static void ** labels[] =
6
    {
7
        &&L0, &&L1, ...
8
    };
9
10
    while (1)
11
    {
12
        goto * labels[getI()];
13
14
        ...
15
L0:
16
        ...
17
L1:
18
        ...
19
    }
20
}

von Karl H. (kbuchegg)


Lesenswert?

Georg-johann L. wrote:


Hmm. Und warum ist das jetzt besser als
1
void foo (void)
2
{
3
  while( 1 )
4
  {
5
    switch( getI() )
6
    {
7
      case 0:
8
        ...
9
        break;
10
11
      case 1:
12
        ...
13
        break;
14
15
      ...
16
17
      default:
18
    }
19
  }
20
}

Mal abgesehen davon, dass mich das default davor schützen
kann, dass getI() etwas ausserhalb des Bereichs zurückliefern
kann und der goto bei dir ins Nirvana geht.

Dir ist hoffentlich schon klar, dass jeder halbwegs ernst zu
nehmende Compiler, so einen switch-case unter der Voraussetzung,
dass die case Label halbwegs 'schöne' Werte haben, in genau so eine
Sprungleiste umwandelt.

Fazit: Viel Lärm um nichts.

von G. L. (sprintersb)


Lesenswert?

Karl heinz Buchegger wrote:
> Hmm. Und warum ist das jetzt besser als
>
1
>     switch( getI() )
2
>
>
> Dir ist hoffentlich schon klar, dass jeder halbwegs ernst zu
> nehmende Compiler, so einen switch-case unter der Voraussetzung,
> dass die case Label halbwegs 'schöne' Werte haben, in genau so eine
> Sprungleiste umwandelt.

Wer behauptet, das sei besser...?

Es ergab sich in einer Situation mit vorgegebener Hardware und 
vorgegebener Funktionalität. Da die Hersteller jedes Cent sparen, war 
der Flash entsprechend knapp und mit switch eben nicht ausreichend -- 
andere Stellen waren schon überarbeitet.

Wenn man ans Resource-Ende kommt, wird die Sache eben zusehends 
aufwändig was Entwicklungszeit, Portierbarkeit, Unterstützung und Nerven 
angeht.

Ich behaupte jetzt mal, GCC ist ein halbwegs ernst zu nehmender 
Compiler. GCC hat mehrer Alternativen zur Umsetzung eines switch wie 
Sprungtabellen, Vergleiche, explizierte binäre Suche.

Man ist also gezwungen, zu schauen was der Compiler so treibt und die 
Quelle des Projekts und/oder des Compilers (falls er quelloffen ist) 
dahingehend anzupassen, dass die verfluchte Software reinpasst. In 
besagtem Falle "und".

von Sven P. (Gast)


Lesenswert?

Mir würds ja schon reichen, wenn der avr-gcc bei "ISR(...) { PORTA = 0; 
}" nicht schon den halben Registersatz wegpusht... aber das is nen 
andres Thema.

Ansonsten geht auch noch schön:
1
if (!machwas()) {
2
  behandle_fehler1();
3
}
4
else if (!machwas_andres()) {
5
  behandle_andren_fehler();
6
}
7
else if (!geh_aufs_klo()) {
8
  mach_in_die_hose();
9
}
10
else {
11
  lass_krachen();
12
}

von UBoot-Stocki (Gast)


Lesenswert?

Hi,

wie ist es so?:
1
if (!machwas())        {  behandle_fehler(_MACHWAS); return(1) }
2
if (!machwas_andres()) {  behandle_fehler(_MACHWAS_ANDRES); return(1)}
3
if (!geh_aufs_klo())   {  behandle_fehler(_GEH_AUFS_KLO); return(1)}
4
lass_krachen();

Gruß

Andreas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

UBoot-Stocki wrote:

> wie ist es so?:

Ist doch auch nur ein verkapptes goto.

von UBoot-Stocki (Gast)


Lesenswert?

Hi,

schon klar - Aber "return" ist doch eine oft gesehene "normale" 
C-Anweisung ? Habe nur versucht, das "strukturierter" zu schreiben.

Gruß

Andreas

von Sven P. (Gast)


Lesenswert?

Machs doch mit If+else, wie ich geschrieben hab... sieht sauber aus und 
funktioniert genauso.

von Εrnst B. (ernst)


Lesenswert?

Ganz nebenbei:
>
1
... return(1) ...
Sollte man sich garnicht erst angewöhnen,
1
return 1;
 ist besser.
Grund: "return" ist keine Funktion, sondern ein unärer Operator, wie 
z.B. auch "~".

Bei return(1) kommt zwar auch das richtige raus, aber wenn man sich 
überlegt was
1
return(1) + 2;
 macht, wird einem auffallen das die Klammern eher kontraproduktiv sind.

von UBoot-Stocki (Gast)


Lesenswert?

Hi,

ich habe es bisher immer wie eine Funktion benutzt:
1
return(1 + 2);

Gruß

Andreas

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.