goto-Label an Funktion übergeben?

OP #2581446
Lesenswert?

Hi,

sagtmal... kann man in C ein goto-Label an eine Funktion übergeben damit 
man aus der Funktion heraus an eine beliebige, vorher definierte Stelle 
hinspringen kann?

so a la
1
void superklassefunktion(label sprungadresse)
2
{
3
  tu_irgendwas_ganz_ganz_tolles();
4
  goto sprungadresse;
5
}
6

7
// ....
8

9
hier_hin_springen_bitte:
10
  tu_was();
11
  tu_nochwas();
12

13
  tu_irgendwas_ganz_ganz_tolles(hier_hin_springen_bitte);


Und bitte keine Moralpredigt über die Verwendung von goto, ich setze es 
verantworungsbewusst ein :)

lg, PoWl
(Firma: JDCC) #2581454
Lesenswert?

Paul Hamacher schrieb:
> Und bitte keine Moralpredigt über die Verwendung von goto, ich setze es
> verantworungsbewusst ein :)

Ok - dann halt nicht ;)

got ist nur innerhalb einer Funktion zulässig (sagen mein Compiler
und dessen Handbuch). Dann müßtest Du auch erst mal die Adresse des
goto herausbekommen ... so etwas wie foo( &goto) dürfte kaum ... :-)
#2581488
Lesenswert?

Joachim Drechsel schrieb:
> so etwas wie foo( &goto) dürfte kaum ... :-)

So schaut's aus, der Adress-Operator kann Adressen von Variablen und 
Strukturen bestimmen, aber nicht die von Funktionen.

Hm, man könnte vielleicht versuchen eine Struktur anzulegen, die als 
Member einen Funktionspointer enthält, von der struct die Adresse 
bestimmen und dann damit Unsinn treiben... aber will man das :-)

Paul Hamacher schrieb:
> Und bitte keine Moralpredigt über die Verwendung von goto, ich setze es
> verantworungsbewusst ein :)

Ja gibt's denn sowas? ;)

Der Wikipedia-Artikel über setjmp und longjmp ist übrigens 
aufschlussreich:
http://en.wikipedia.org/wiki/Setjmp.h
#2581657
Lesenswert?

> Hm, tatsächlich kann man in dem nachfolgenden Beispiel den
> Adressoperator vor das add; schreiben. Ich kannte es aber immer nur
> ohne. Wieder was gelernt.

Bei K&R-C war das & nötig, später konnte es als Erleichterung 
weggelassen werden.
Mit einem Funktionsnamen kann man ja eh nur entweder mit () die Funktion 
aufrufen, oder ihre Adresse bilden; insofern ist das & überflüssig, weil 
es ohne die () nur um die Adresse gehen kann.
#2581668
Lesenswert?

Die (ursprüngliche) innere Logik: Der Name einer Funktion ist deren 
Adresse, der Operator () ist die Dereferenzierung des Zeigers auf eine 
Funktion.

Deshalb ist
  add(2,3)
und
  f(2,3)
im Grunde der gleiche Vorgang: links die Adresse einer Funktion, rechts 
deren Dereferenzierung. Im einen Fall ist die Adresse konstant, im 
anderen Fall nicht. Da aber nicht jeder Programmierer in Grammatiken 
denkt, hat man später auch &/* zugelassen.
OP #2581703
Lesenswert?

Das mit dem Stack-Salat ist in der Tat ein Problem, das hab ich garnicht 
bedacht. Ideal wäre es, wenn er direkt NACH der Funktion an die 
angegebene Adresse springen würde.

Einsatzzweck: Es geht um eine Eingabemaske auf einem LCD. Man kann 
mehrere Zahlen eingeben. Die Zahleneingabe erfolgt indem das Programm in 
eine Funktion hineinspringt, welche die Tastatureingaben verarbeitet und 
an vorgegebener Stelle auf dem LCD ausgibt. Sobald die Zahl eingegeben 
wurde ist die Funktion zuende und es geht weiter im Code. Wenn man 
jedoch in der Eingabemaske einen bereits eingegebenen Wert ändern möchte 
muss man im Programm zurückspringen können, und zwar immer einen 
Eingabeschritt zurück. Da eignet sich meines Erachtens nur goto für, 
denn eine Schleife kann immer nur in eine Richtung springen. Die Idee, 
die Sprungadresse an die Zahleneingabefunktion mit zu übergeben könnte 
mir helfen, aus der Funktion heraus direkt eine Eingabe 
zurückzuspringen. Aber ja, da ist das Stack-Problem, da er sich ja dann 
nur noch tiefer in eine Funktion nach der anderen verschachtelt.

Edit:
Ach jetzt schimpft nicht so über goto! ;) Manchmal hat man eben keine 
Lust jedes mal eine Merker-Variable mitzuschleppen wenn man an 
geeigneter Stelle eine Sprungfunktion einsetzen kann. Spaghetticode ist 
nix gut aber eine lange Nudel mit nem kleinen Knoten drin ist noch lange 
keine Spaghetti.
Gast #2581712
Lesenswert?

Ralf G. schrieb:
> Paul Hamacher schrieb:
>> Und bitte keine Moralpredigt über die Verwendung von goto, ich setze es
>> verantworungsbewusst ein :)
>
> Darf ich trotzdem mal nach einer sinnvollen Anwendung für diese
> Konstruktion fragen? Vor allem: Wie kommt man mit dem 'Stack-Salat'
> klar?

Darüber ist er sich wahrscheinlich gar nicht im klaren.
Gast #2581715
Lesenswert?

> Einsatzzweck: Es geht um eine Eingabemaske auf einem LCD. Man kann
> mehrere Zahlen eingeben.
dafür willst du gotos verwenden?

ist es nicht viel einfacher eine sich in einer Variabel sich die Zahl zu 
merken die gerade geändert wird?

int Ziffer = 1
while( editmod = 1 ) {
  if ( rechts && Ziffer > 1 ) {
      ZIffer--;
  }

  if ( links && Ziffer < x ) {
      ZIffer++;
  }

  lese_ziffer( Ziffer );
}
#2581717
Lesenswert?

1
while (sonstwas)
2
{
3
 switch (pos)
4
 {
5
  case 0:
6
   // diverse Tests
7
   // Funktionsaufrufe
8
   break;
9
  case 1:
10
   // diverse Tests
11
   // Funktionsaufrufe
12
   break;
13
  case 2:
14
   // diverse Tests
15
   // Funktionsaufrufe
16
   break;
17

18
// ...
19

20
  default:
21
   // diverse Tests
22
   // Funktionsaufrufe
23
   break;
24
 }
25
}
Macht man da nicht sowas? 'pos' kann man zu jeder Zeit 
de-/inkrementieren oder einen neuen Wert zuweisen und sich so durch ein 
Menü hangeln.
#2581735
Lesenswert?

Ralf G. schrieb:

> Macht man da nicht sowas?

Kann man.
Man kann aber auch eine schöne Struktur erfinden, die ein Eingabefeld 
beschreibt und dann für eine Eingabeseite ein Array aus derartigen 
Objekten machen.
Das Vorwärts/Rückwärtsspringen durch die Eingabefelder ist dann nichts 
anderes als das Inkrementieren bzw. Dekrementieren des aktuellen Index, 
welcher darüber entscheidet, welches dieser Beschreibungsobjekte an die 
Funktion übergeben wird, welche die Benutzeringaben überwacht und die 
Wertemanipulation vornimmt. Kommt die Funktion zurück, gibt sie Auskunft 
darüber, wie der Benutzer entschieden hat: will er zum vorhergehenden 
Eingabefeld, zum nachfolgenden oder ist er mit der Maske überhaupt 
fertig.

Das ganze ist Codemässig ziemlich banal, das eigentliche Aussehen der 
Maske mit all ihren Eingabefeldern findet sich an einer Stelle im Code 
wieder und besteht einzig aus einer Zusammenfassung der Daten, die die 
Maske ausmachen.

Und das beste daran. Eine weitere Eingabemaske ist einfach nur ein 
weiteres Array, in dem der Aufbau der Maske beschrieben ist :-)


Es gibt Fälle, in denen ein goto tatsächlich eine gute Lösung ist. Mir 
ist nur in 28 Jahren kein einziger davon untergekommen :-)
(Um der Wahrheit genüge zu tun. Ich hab einmal einen goto benutzt. Muss 
so 15 Jahre her sein. Aber selbst den haben wir dann nach ein paar 
Jahren entfernt und durch etwas besseres ersetzt)
Gast #2581751
Lesenswert?

Paul Hamacher schrieb:
> Da eignet sich meines Erachtens nur goto für

Ach du bist der, für den die ganzen Warnungen geschrieben wurden! GoTo 
kann selten wirklich sinnvoll eingesetzt werden. Und deine 
Umsetzungsidee ist ehrlich gesagt eine der schlechtesten Möglichkeiten 
es zu verwenden von denen ich je gehört habe.
Auf jeden Fall nicht, so wie du denkst, eine gute Idee.
Moderator Persönliche Seite #2581766
Lesenswert?

A. K. schrieb:
> Klaus Wachtler schrieb:
>
>> Bei K&R-C war das & nötig, später konnte es als Erleichterung
>> weggelassen werden.
>
> Ich habs andersrum in Erinnerung. Das & wurde später für jene
> hinzugefügt, die sonst konfus wurden.

War mir auch nicht so klar, stimmt aber offenbar.  Den "Ur-K&R"
(der eigentlich nur ein "R" ist) gibt's auf Dennis Ritchie's Seite
bei AT&T:

http://cm.bell-labs.com/cm/cs/who/dmr/cman.pdf

Da steht eindeutig, dass & nur auf Objekte anzuwenden ist, und
dass die Adresse einer Funktion automatisch dann gebildet wird,
wenn ein Funktionsname ohne die runden Klammern für deren Aufruf
benutzt wird.

Karl Heinz Buchegger schrieb:
> Es gibt Fälle, in denen ein goto tatsächlich eine gute Lösung ist. Mir
> ist nur in 28 Jahren kein einziger davon untergekommen :-)

Für klassisches "goto": vorzeitiges Rausspringen aus einer Funktion,
die sonst durch stark verschachtelte if-Anweisungen extrem unüber-
sichtlich würde.  Natürlich nur dann, wenn es ein einfaches "return"
(das ja ein verkapptes "goto" ist) nicht auch tun würde, weil
bswp. noch per malloc() allozierte Daten vorher zu beräumen sind.

Für setjmp/longjmp: Fehlerbehandlung; letztlich lassen sich C++-
Exceptions damit implementieren (im GCC-Jargon "sjlj" genannt),
und etwas Vergleichbares könnte man natürlich genauso für plain C
aufsetzen.
#2581776
Lesenswert?

Klaus Wachtler schrieb:

> Nicht eher "*f" statt "f*"?
> PS: hast du gerade noch die Kurve gekriegt!

Wär freilich besser gewesen, C hätte diese Kurve nicht gekriegt. Mit der 
Dereferenzierung als Präfixoperator hängt der ganze Wirrwar schwer 
verständlicher Typdefinitionen zusammen. IIRC wollten sie es zunächst 
auch als Postfixoperator implementieren, merkten aber, dass a*-b dann 
zweideutig ist.
#2581800
Lesenswert?

Jörg Wunsch schrieb:
> Für klassisches "goto": vorzeitiges Rausspringen aus einer Funktion,
> die sonst durch stark verschachtelte if-Anweisungen extrem unüber-
> sichtlich würde.  Natürlich nur dann, wenn es ein einfaches "return"
> (das ja ein verkapptes "goto" ist) nicht auch tun würde, weil
> bswp. noch per malloc() allozierte Daten vorher zu beräumen sind.

Und die eigentliche Rücksprungadresse der Funktion auf dem Stack?

Diese 'Bastel'-Exeptions, könnte man die nicht auch so anwenden:
1
// 'try'
2
switch(x)
3
{
4
 default:
5
// Code
6
// ...
7
// noch mehr Code
8
// ...
9
// hier Code für 'was schiefgegangen'
10
// Fehlernummer setzen
11
  break;
12
// wieder Code
13
// ...
14
  break;
15
}
16

17
// 'catch'
18
 if (Fehler)
19
 {
20
// ...
21
 }
22
// schön aufräumen!
23
// ...
24
 return Fehler;
25
}
Ich hoffe, so ein Mehrfach-'break' geht genauso, wie Mehrfach-'return'.
#2581814
Lesenswert?

Jörg Wunsch schrieb:

> War mir auch nicht so klar, stimmt aber offenbar.  Den "Ur-K&R"
> (der eigentlich nur ein "R" ist) gibt's auf Dennis Ritchie's Seite
> bei AT&T:

Thx. Das ist noch die Version mit a =+ 1 statt a += 1.

Besonders gut gefällt mir dieser Satz: "In PDP-11 C none of this 
nonsense is necessary and the extern specifier is ignored in external 
definitions." Den Sachverhalt kannte ich schon, aber noch nie schön 
formuliert. Heute geht es ja meist nur auf die unsinnige Weise. ;-)
#2581846
Lesenswert?

Ralf G. schrieb:
> Das ist ja ein Sprung ans Ende. Nicht aus der Funktion raus!
>
> Jörg Wunsch schrieb:
>> Für klassisches "goto": vorzeitiges Rausspringen aus einer Funktion,

Es ist aber genau das, was Jörg meinte. Ein Sprung ans Ende der 
Funktion, um sie vorzeitig zu verlassen. Das, was du angenommen hast 
(ein goto aus der Funktion raus), existiert ja auch gar nicht.
Persönliche Seite #2581992
Lesenswert?

In GNU-C kann man Adressen von Labels nehmen und goto indirekt 
verwenden:
1
int weird_goto (unsigned x)
2
{
3
    static void **labels[] = { &&a, &&b, &&c };
4

5
    if (x >= sizeof (labels) / sizeof (labels[0]))
6
        return -1;
7

8
    goto *labels[x];
9

10
    a:  return 1;
11
    b:  return 2;
12
    c:  return 0;
13
}
Ob computed goto sinnvoll ist — etwa in auto-generiertem Code — sei 
dahingestellt. goto selbst ist in auto-generiertem Code jedenfalls 
häufiger anzutreffen als in handgeschriebenem. GCC zum Beispiel 
verwendet goto ausgiebig, etwa in insn-recog.c.

Solche Label-Adressen sind nicht sinnvoll an Funktionen zu übergeben, 
auch nicht wenn diese innerhalb von weird_goto verwendet werden.

Auf nonlocal goto aka. setjmp/longjmp um z.B. Ausnahmen nachzubilden 
wurde oben bereits hingewiesen.
Moderator Persönliche Seite #2582225
Lesenswert?

Johann L. schrieb:
> Ob computed goto sinnvoll ist — etwa in auto-generiertem Code — sei
> dahingestellt.

Computed goto braucht man für ordentliches FORTRAN-Feeling. :-)

Stefan Ernst schrieb:
>> Das ist ja ein Sprung ans Ende. Nicht aus der Funktion raus!

> Es ist aber genau das, was Jörg meinte.

Ja, sorry, wenn das beim mir nicht eindeutig klang.  Genau so meinte
ich es, wie Stefan das oben am Beispiel demonstriert hat.  Das halte
ich für eine der wenigen Anwendungen, bei denen ein paar gotos für
mehr Klarheit sorgen können, als wenn man diverse if-Anweisung immer
tiefer schachteln würde.

Von Donald E. Knuth gibt's ja auch einen Artikel "Structured
Programming with GOTO".
#2582653
Lesenswert?

Mit setjmp/longjmp kann man beim AVR-GCC sogar das bei Anfängern 
allseits so beliebte Springen aus einem Interrupt machen.
Es merkt sich nicht nur den Stackpointer, sondern auch das SREG.
Beim 8051 geht das nicht, da läßt sich ein Interrupt nur mit RETI 
beenden.

Das goto muß sich nichts merken, da ja innerhalb einer Funktion nichts 
am Stack gedreht wird.

Das goto ist ganz nützlich innerhalb eines switch/case, wenn am Ende 
einiger case immer das gleiche gemacht werden muß.
In dem Fall ist goto schöner als copy&paste.
Das switch/case verhält sich ja selber wie ein goto.


Peter
#2582840
Lesenswert?

Paul Hamacher schrieb:
> Sobald die Zahl eingegeben
> wurde ist die Funktion zuende und es geht weiter im Code.

Das ist dann quasi eine Statemaschine, wo der Programmcounter der 
aktuelle State ist.
Sowas wird früher oder später eine Sackgasse. Irgendwas dürfen nicht 
mehr alle Aufgaben stehen, nur weil man gerade etwas eingibt.


Karl Heinz Buchegger schrieb:
> Man kann aber auch eine schöne Struktur erfinden, die ein Eingabefeld
> beschreibt und dann für eine Eingabeseite ein Array aus derartigen
> Objekten machen.

Ja, genau das ist die übliche Methode. Die Eingabefunktion guckt nur 
kurz nach, ob eine Taste gedrückt wurde und macht die entsprechende 
Aktion. Danach gehts mit den anderen Tasks weiter.

Blockierende Schleifen gibt es nicht in einer Task. Jede Task arbeitet 
abweisend (if gerade nichts zu tun: return).
Nur die Mainloop ist die einzige Endlosschleife.


Peter
#2582900
Lesenswert?

Johann L. schrieb:
> In GNU-C kann man Adressen von Labels nehmen und goto indirekt
> verwenden:
>
1
int weird_goto (unsigned x)
2
> {
3
>     static void **labels[] = { &&a, &&b, &&c };
4
> 
5
>     if (x >= sizeof (labels) / sizeof (labels[0]))
6
>         return -1;
7
> 
8
>     goto *labels[x];
9
> 
10
>     a:  return 1;
11
>     b:  return 2;
12
>     c:  return 0;
13
> }


Nette Sache. Mit anderen Compilern geht das nicht? Sonst wäre es ein 
klarer Contender für den IOCCC. ;-)
#2582916
Lesenswert?

Zumindest könnte man einmal über die Labels laufen, um mehrere 
sigjmp_buf zu füllen und die dann später für die Sprünge nehmen.
Ich glaube, es steht nirgends geschrieben, daß man ein sigjmp_buf nur 
einmal nehmen darf :-)

Das wäre zumindest auf dem Papier ziemlich portabel.
#2582965
Lesenswert?

Mark Brandis schrieb:

> Nette Sache. Mit anderen Compilern geht das nicht?

Nope. Ist kein Standard-C

Wenn man sowas nicht hat, nimmt man einfach zb ein switch-case oder eine 
andere Konstuktion mit der dasselbe erreichbar ist.  :-)
1
int weird_goto (unsigned x)
2
{
3
  switch( x )
4
  {
5
    case 0:
6
      return 1;
7

8
    case 1:
9
      return 2;
10

11
    case 2:
12
      return 0;
13

14
    default:
15
      return -1;
16
  }
17
}

Zugegebe: Ist manchmal etwas mehr Schreibaufwand, aber so schlimm ist es 
dann auch wieder nicht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren