www.mikrocontroller.net

Forum: GCC GOTO oder While-Schleife? Was ist Besser oder einfacher?

Autor: Axel Lemke (lemmi4711)
Datum: 11.05.2008 18:03

Mal was ganz rhetorisches...

Was meint Ihr?

Die Frage kam mir in den Sinn, da ein sogenannter Gast-Poster meinen
Sourcecode "bemängelte"...
Autor: Johannes M. (johnny-m)
Datum: 11.05.2008 18:13

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)
Autor: Wolfgang Mües (Gast)
Datum: 11.05.2008 18:23

"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.
Autor: Gast (Gast)
Datum: 11.05.2008 18:30

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.
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 11.05.2008 22:53

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.
Autor: Stefan Salewski (Gast)
Datum: 11.05.2008 23:03

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

http://de.wikipedia.org/wiki/Sprunganweisung
Autor: Bernhard R. (barnyhh)
Datum: 12.05.2008 10:04

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
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 12.05.2008 21:31

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.
Autor: Paul Hamacher (powl)
Datum: 12.05.2008 22:17

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
Autor: yalu (Gast)
Datum: 13.05.2008 10:09

> 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.
Autor: Paul Hamacher (powl)
Datum: 13.05.2008 13:40

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

wenn nicht, muss ich mich damit nochmal beschäftigen
lg PoWl
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 13.05.2008 13:45

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).
Autor: Andreas Kaiser (a-k)
Datum: 13.05.2008 13:50

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.
Autor: Peter Dannegger (peda)
Datum: 13.05.2008 15:13

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
Autor: Bobby (Gast)
Datum: 13.05.2008 17:02

@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...
Autor: Rolf Magnus (Gast)
Datum: 13.05.2008 17:44

>> 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.
Autor: I_ H. (i_h)
Datum: 13.05.2008 21:25

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 ;-)
Autor: Axel Lemke (lemmi4711)
Datum: 20.05.2008 18:51

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.
Autor: Karl heinz Buchegger (kbuchegg) (Moderator)
Datum: 20.05.2008 19:01

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.
Autor: Rolf Magnus (Gast)
Datum: 20.05.2008 19:05

> 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?
Autor: I_ H. (i_h)
Datum: 20.05.2008 19:15

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.
Autor: Benedikt K. (benedikt)
Datum: 20.05.2008 20:20

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 !
Autor: G. L. (sprintersb)
Datum: 20.05.2008 23:04

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.
void foo (void ** label)
{
    goto *label;
}

avr-gcc bastelt daraus folgerichtig:
foo:
  push r24   ;  11  indirect_jump/2  [length = 3]
  push r25
  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
Autor: Rufus t. Firefly (rufus) (Moderator)
Datum: 21.05.2008 08:03

>
> void foo (void ** label)
> {
>     goto *label;
> }
> 

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

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.
Autor: Peter Dannegger (peda)
Datum: 21.05.2008 09:28

Georg-johann L. wrote:

>
> void foo (void ** label)
> {
>     goto *label;
> }
> 

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
Autor: G. L. (sprintersb)
Datum: 21.05.2008 12:26

Rufus t. Firefly wrote:
>>
>> void foo (void ** label)
>> {
>>     goto *label;
>> }
>> 
>
> [...] 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.
extern unsigned int getI (void);

void foo (void)
{
    static void ** labels[] =
    {
        &&L0, &&L1, ...
    };

    while (1)
    {
        goto * labels[getI()];

        ...
L0:
        ...
L1:
        ...
    }
}
Autor: Karl heinz Buchegger (kbuchegg) (Moderator)
Datum: 21.05.2008 12:37

Georg-johann L. wrote:


Hmm. Und warum ist das jetzt besser als
void foo (void)
{
  while( 1 )
  {
    switch( getI() )
    {
      case 0:
        ...
        break;

      case 1:
        ...
        break;

      ...

      default:
    }
  }
}

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.
Autor: G. L. (sprintersb)
Datum: 21.05.2008 13:46

Karl heinz Buchegger wrote:
> Hmm. Und warum ist das jetzt besser als
>
>     switch( getI() )
> 
>
> 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".
Autor: Sven Pauli (haku) Benutzerseite
Datum: 21.05.2008 13:56

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:
if (!machwas()) {
  behandle_fehler1();
}
else if (!machwas_andres()) {
  behandle_andren_fehler();
}
else if (!geh_aufs_klo()) {
  mach_in_die_hose();
}
else {
  lass_krachen();
}
Autor: UBoot-Stocki (Gast)
Datum: 21.05.2008 16:51

Hi,

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

Gruß

Andreas
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 21.05.2008 16:59

UBoot-Stocki wrote:

> wie ist es so?:

Ist doch auch nur ein verkapptes goto.
Autor: UBoot-Stocki (Gast)
Datum: 21.05.2008 17:04

Hi,

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

Gruß

Andreas
Autor: Sven Pauli (haku) Benutzerseite
Datum: 21.05.2008 17:11

Machs doch mit If+else, wie ich geschrieben hab... sieht sauber aus und
funktioniert genauso.
Autor: Ernst Bachmann (ernst)
Datum: 21.05.2008 17:12

Ganz nebenbei:
>
... return(1) ... 
Sollte man sich garnicht erst angewöhnen,
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
return(1) + 2;
 macht, wird einem auffallen das die Klammern eher kontraproduktiv sind.
Autor: UBoot-Stocki (Gast)
Datum: 21.05.2008 17:28

Hi,

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

Gruß

Andreas

Antwort schreiben

Die Angabe einer Email-Adresse ist freiwillig. Wenn Sie automatisch per Email über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
  • Aussagekräftigen Betreff wählen
  • Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
  • JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
  • Schaltpläne, Screenshots usw. als PNG oder GIF anhängen

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel






webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net