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
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 ... :-)
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
Mark Brandis schrieb:> So schaut's aus, der Adress-Operator kann Adressen von Variablen und> Strukturen bestimmen, aber nicht die von Funktionen.
warum denn nicht, wie sollte sonst funktionzeiger funktionieren?
Aber auch damit kann man nur sinnvoll innerhalb eine Funktion springen
(und auch da nur bedingt), aber nicht in f1() ein Label merken, an f2()
übergeben und aus f2() an das Label in f1() springen - das geht nicht
gut (auch wenn es zufällig mal so aussehen mag).
Peter II schrieb:> warum denn nicht, wie sollte sonst funktionzeiger funktionieren?
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.
> 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.
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.
Der Systematik halber gehören zusammen:
f = &add; (*f)(3,2)
oder alternativ die klassische Variante:
f = add; f(3,2)
A. K. schrieb:> Ich habs andersrum in Erinnerung. Das & wurde später für jene> hinzugefügt, die sonst konfus wurden.
Tja, wir mit K&R-Anfängen kommen langsam in das senile Alter.
:-)
>> Der Systematik halber gehören zusammen:> f = &add; (f*)(3,2)
Nicht eher "*f" statt "f*"?
PS: hast du gerade noch die Kurve gekriegt!
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.
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?
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?
Das ist leicht:
Eine Methode um sich selbst möglichst kryptisch und unleserlich
möglichst tief ins eigene Knie zu schiessen.
Karl Heinz Buchegger schrieb:> Das ist leicht:> Eine Methode um sich selbst möglichst kryptisch und unleserlich> möglichst tief ins eigene Knie zu schiessen.
'Kryptisch und unleserlich' - das ist der 1.Teil ...
Ralf G. schrieb:> 'Kryptisch und unleserlich' - das ist der 1.Teil ...
Und der zweite?
Durch wildes hin und herspringen in und aus Funktionen jeden total
verwirren (auch den Prozessor g)
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.
Paul Hamacher schrieb:> Da eignet sich meines Erachtens nur goto für,
Aber nur deines Erachtens.
Mit einer State machine z.B. kann man das ganz ohne gotos machen.
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.
> 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 );
}
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)
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.
Erstmal weg vom 'goto'! ;-)
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.
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.
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.
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
returnFehler;
25
}
Ich hoffe, so ein Mehrfach-'break' geht genauso, wie Mehrfach-'return'.
Ralf G. schrieb:> Und die eigentliche Rücksprungadresse der Funktion auf dem Stack?
Was soll damit sein?
Jörg meint einen Sprung innerhalb der Funktion. So etwas:
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. ;-)
Stefan Ernst schrieb:> Was soll damit sein?> Jörg meint einen Sprung innerhalb der Funktion. So etwas:> ... ... ...
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,
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.
In GNU-C kann man Adressen von Labels nehmen und goto indirekt
verwenden:
1
intweird_goto(unsignedx)
2
{
3
staticvoid**labels[]={&&a,&&b,&&c};
4
5
if(x>=sizeof(labels)/sizeof(labels[0]))
6
return-1;
7
8
goto*labels[x];
9
10
a:return1;
11
b:return2;
12
c:return0;
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.
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".
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
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
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.
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
intweird_goto(unsignedx)
2
{
3
switch(x)
4
{
5
case0:
6
return1;
7
8
case1:
9
return2;
10
11
case2:
12
return0;
13
14
default:
15
return-1;
16
}
17
}
Zugegebe: Ist manchmal etwas mehr Schreibaufwand, aber so schlimm ist es
dann auch wieder nicht.