Schönen guten Abend
Ich habe eine kleines C-Programm
1
for(Schleifenbedingung)
2
{
3
Befehl;
4
Befehl;
5
while(Bedingung)
6
{
7
Befehl;
8
if(Abfrage)break;//verlasse beide Schleifen
9
Befehl;
10
}
11
}
Wenn „Abfrage“ erfüllt ist, möchte ich nicht
nur die while-Schleife sondern auch sofort die
for-Schleife verlassen. Mit break wird ja jetzt
nur die while-Schleife verlassen.
Gibt es eine Lösung ohne goto ;-) ?
Grübler schrieb:> Wenn „Abfrage“ erfüllt ist, möchte ich nicht nur die while-Schleife> sondern auch sofort die for-Schleife verlassen.
Pack das Ganze in ein Unterprogramm und mach ein return aus dem
break... ;-)
Kermit schreibt:
>Warum kein goto?
Goto ist verpönt, da man damit
sehr unübersichtlichen Programmcode
erzeugen kann (Spagetti-Programmierung).
In JAVA gibt es z.B. gar kein goto mehr.
Ich werde aber hier doch ein goto nehmen,
und das Sprungziel direkt unter die Schleifen
setzten. Das scheint mir gut lesbar zu sein.
Danke an alle für die Tipps!
ich seh auch kein Problem mit goto. Ja, man kann viel Blödsinn damit
anstellen. Das heisst aber nicht, dass man es auch macht, wenn man es
benutzt.
Ja, man kann es immer vermeiden. Muss man aber nicht:-)
Grübler schrieb:> Goto ist verpönt, da man damit> sehr unübersichtlichen Programmcode> erzeugen kann (Spagetti-Programmierung).
Kreissägen führen bei unsachgemäßer Verwendung regelmäßig zu schweren
Verletzungen, dennoch sind sie nicht "verpönt".
Du lieferst selbst das beste Beispiel, warum ein pauschales "goto ist
böse" schwachsinnig ist.
> In JAVA gibt es z.B. gar kein goto mehr.
Da gibt es aber ein break mit Label, eben weil der Abbruch von
verschachtelten Schleifen sonst unübersichtlich und fehleranfällig wird
(Pascal lässt grüßen...).
Wenn man es drauf anlegt kann man damit genauso üblen Code produzieren
wie mit goto in C. Eigentlich sogar noch übler, da das Label bei diesen
Konstrukten dann nichtmal an der Stelle steht, wo wirklich hingesprungen
wird. Es steht nur nicht das angeblich böse "goto" da.
Andreas
Grübler schrieb:> Gibt es eine Lösung ohne goto ;-) ?
Nein, es gibt keine Lösung, die gleich effektiv ist.
Die mit Return kostet etwas Stack, ist aber gleich gut leserlich, wie
das Goto. Und dann kommen aber die Nörgler, die sagen, daß jede Funktion
nur ein Return haben soll.
Die mit ner Variable macht den Code deutlich unleserlicher und kann
sogar richtig teuer werden, wenn extra für diese zusätzliche Variable
ein Stackframe angelegt werden muß.
Man sollte Goto nicht unnütz nehmen. Wenn es aber paßt, wie die Faust
aufs Auge, dann nimmt man es einfach.
Irgendwelche Dogmen auszusprechen und sich sklavisch daran
festzukrallen, halte ich für Schwachsinn. Der gesunde Menschenverstand
sollte Vorrang haben.
Ein ähnliches Problem hat man auch bei Switch-Anweisungen in einer
Schleife.
Sehr oft gibt es einen oder mehrere Case, die die Schleife verlassen.
Durch die Doppelfunktion des Break ist das aber nicht möglich
Daher findet man Goto auch sehr oft in Switch-Anweisungen.
Sehr sinnvoll wäre daher ein extra Schlüsselwort zum Beenden eines Case
gewesen.
Peter
ich wette, dass 10k mal öfter gesagt wird goto ist scheiße, als
verkackter code mit goto gelesen wird.
oder:
ich wette, dass 99% der leute, die sagen goto ist verpönt, noch nie
verkackten goto-code gesehen haben.
und schon ist das goto weg ;-)
wenn es passt und den code leserlicher gestaltet, verwende es.
weenn es dein code ist - mach was du willst, wenn dir dein arbeitgeber
ein goto verbieten möchte/verbietet hast du zwei möglichkeiten:
- gut argumentieren und eine rebellion veranstalten
- akzeptieren und hässliche/schlecht lesbare/sonstige workarounds
einbauen...
Peter Dannegger schrieb:> Die mit Return kostet etwas Stack, ist aber gleich gut leserlich, wie> das Goto. Und dann kommen aber die Nörgler, die sagen, daß jede Funktion> nur ein Return haben soll.
Jepp, und die Nörgler haben oft recht. Wie toll ist es wenn Du am Ende
einer Funktion einen Breakpoint setzen willst und musst dann 2 oder 3
oder 7 setzen, weil entsprechend viele returns da sind.
Wie toll ist es wenn du am Ende der Funktion schnell einen Trace
rausschreiben willst und kannst auch das dann 3 oder gar 7 mal machen.
Peter Dannegger schrieb:> Die mit ner Variable macht den Code deutlich unleserlicher und kann> sogar richtig teuer werden, wenn extra für diese zusätzliche Variable> ein Stackframe angelegt werden muß.
Der Code von Markus war keinen Deut schlechter. In Sachen Effizienz gabe
ich dir 100% recht, wobei das auf µCs manchmal eine Rolle spielt, auf
PCs nur in absoluten Ausnahmefällen.
Peter Dannegger schrieb:> Man sollte Goto nicht unnütz nehmen. Wenn es aber paßt, wie die Faust> aufs Auge, dann nimmt man es einfach.>> Irgendwelche Dogmen auszusprechen und sich sklavisch daran> festzukrallen, halte ich für Schwachsinn. Der gesunde Menschenverstand> sollte Vorrang haben.
100% agree, deshalb habe ich oben auch nur oft gesagt :-)
benjamin tülpchen schrieb:> ich wette, dass 99% der leute, die sagen goto ist verpönt, noch nie> verkackten goto-code gesehen haben.
Ich habe mal ein paar Jahre Code warten müssen, der von einem
selbstgeschriebenen Cross-Compiler (nicht von mir) aus IBM-Host
Assembler C für Unixsysteme und Windows und OS/2 erzeugt hat. So grausam
kann kein Mensch C Spagetticode programmieren, nicht mal einer der sonst
nur Basic programmiert hat :-).
Gruß
benjamin tülpchen schrieb:> ich wette, dass 10k mal öfter gesagt wird goto ist scheiße, als> verkackter code mit goto gelesen wird.
Wohl wahr.
Aber die Regel ist so leicht, daß jeder sie nachplappern kann.
Ähnlich wie "Makros sind verboten".
Denken statt Abklappern von Regel-Checklisten ist halt aufwendiger.
Das Ganze auf die Spitze getrieben heisst dann MISRA-C.
Ein "goto" ist wie Gift, genauso wie vieles gute in Medizin. Es gibt
Programme die dankt dem Befehl gut strukturiert und gut lesbar sind.
Z.B. am Anfang prüft man die Plausibilität von sehr vielen Angaben. Wenn
etwas nicht passt dann lädt man Puffer für Anzeige eine Meldung und mit
einem Sprung „goto Ergebnis“ verlässt man egal wie tief verschaltete
Schleife. Im Ergebnis prüft man dann ob etwas zum Schlissen (geöffnete
Files) oder zum Freigeben (Speicher).
U.R. Schmitt schrieb:> Peter Dannegger schrieb:>> Die mit Return kostet etwas Stack, ist aber gleich gut leserlich, wie>> das Goto. Und dann kommen aber die Nörgler, die sagen, daß jede Funktion>> nur ein Return haben soll.>> Jepp, und die Nörgler haben oft recht. Wie toll ist es wenn Du am Ende> einer Funktion einen Breakpoint setzen willst und musst dann 2 oder 3> oder 7 setzen, weil entsprechend viele returns da sind.> Wie toll ist es wenn du am Ende der Funktion schnell einen Trace> rausschreiben willst und kannst auch das dann 3 oder gar 7 mal machen.
Da lässt sich aber drüber streiten. Man kann eine Funktion auch ganz
schön unübersichtlich machen, wenn man aus Krampf nur ein return haben
möchte. Das gibt dann auch gerne Spaghetticode. Der Königsweg wäre
natürlich, per goto zu einem Return zu springen ;-)
Nico
Die Qunitessenz ist wie so oft:
Diese Daumenregeln sind grundsätzlich nicht schlecht. Sie geben einem
Anfänger Richtlinien, wie er die gröbsten Source-Code Fehler vermeiden
kann. Beachtet man sie, dann läuft man nicht so leicht in Gefahr den zu
Recht gefürchteten 'Spaghetti-Code' zu produzieren.
Aber wie so oft sind derartige Regeln nicht als Dogma zu sehen, von dem
man nicht im Einzelfall abweichen kann, solange man weiß was man tut und
einen guten Grund dafür hat.
Also ich hasse generell Schleifen, die mittendrin verlassen werden. Das
ist immer schwer zu verstehen. Das obige Beispiel würde ich selbst daher
so schreiben:
Nico22 schrieb:> Da lässt sich aber drüber streiten. Man kann eine Funktion auch ganz> schön unübersichtlich machen, wenn man aus Krampf nur ein return haben> möchte. Das gibt dann auch gerne Spaghetticode.
Jepp, ganz klar, wie Peter gesagt hat:
> Irgendwelche Dogmen auszusprechen und sich sklavisch daran> festzukrallen, halte ich für Schwachsinn. Der gesunde Menschenverstand> sollte Vorrang haben.> Königsweg wäre natürlich, per goto zu einem Return zu springen ;-)
***ROFL***
Das ist in java toll. Du klammerst das mit einem try finally und kannst
dort dann alles sauber beenden, egal wie verschachtelt das Ganze ist.
Das ist nämlich das nächste Problem mit mehreren returns. Du hast eine
komplexere Aktion, z.B: liest Du aus einer Datei/Dateien und/oder
MQ-Queues Daten und speicherst Sie (abhängig von dem was Du gelesen
hast, also komplexe Bedingungen und ggf. Schleifen) in eine Datenbank
und oder andere Queues. Am Ende musst Du alles aufräumen und je nachdem
ob es geklappt hat in MQ und Datenbank commit oder rollback absetzen.
Dann noch mehrere Returns und deine Funktion/Methode wird nie richtig
funktionieren.
Mit try finally problemlos in den Griiff zu kriegen.
Aber Java in µCs ist auch nicht meine Präferenz :-)
U.R. Schmitt schrieb:>> Königsweg wäre natürlich, per goto zu einem Return zu springen ;-)> ***ROFL***>> Das ist in java toll. Du klammerst das mit einem try finally und kannst> dort dann alles sauber beenden, egal wie verschachtelt das Ganze ist.
Solange dich nicht interessiert, wo innerhalb des Blocks die Exception
aufgetreten ist. Sonst mußt du halt um jede Funkton ein eigenes try
setzen.
> Das ist nämlich das nächste Problem mit mehreren returns. Du hast eine> komplexere Aktion, z.B: liest Du aus einer Datei/Dateien und/oder> MQ-Queues Daten und speicherst Sie (abhängig von dem was Du gelesen> hast, also komplexe Bedingungen und ggf. Schleifen) in eine Datenbank> und oder andere Queues. Am Ende musst Du alles aufräumen und je nachdem> ob es geklappt hat in MQ und Datenbank commit oder rollback absetzen.> Dann noch mehrere Returns und deine Funktion/Methode wird nie richtig> funktionieren.> Mit try finally problemlos in den Griiff zu kriegen.
Mit goto auch.
> Oh ja, finally fehlt mir in C++.
Warum? Dank vernünfig funktionierender Destruktoren braucht man das doch
gar nicht.
Nico22 schrieb:> Oh ja, finally fehlt mir in C++.
Brauchen wir auch nicht.
Wir haben einen Destruktor, der auch so funktioniert wie er soll.
RAII und du brauchst kein finally mehr
http://www.research.att.com/~bs/bs_faq2.html#finally
damit verlieren dann auch mehrere return in einer Funktion ihren
"Schrecken".
z.B. schlichte Debugausgaben nach dem Motto "ich komme gerade hier
vorbei und springe zurück..." oder halt alles, was mit Aufräumen zu tun
hat.
Letzteres kann man natürlich immer in einem dtor verpacken, aber
das lohnt nicht in jedem Fall.
Also hat man dann doch manchmal in mehreren catch-Zweigen mehr
oder weniger Codewiederholung, die in einem finally-Block besser
aussehen würden.
Nachtrag: am ehesten vermisse ich es dann, wenn ich etwas machen
will, was nicht direkt mit der eigentlichen Programmlogik verwebt
ist, z.B. Laufzeitmessung eines Algos.
Klaus Wachtler schrieb:> naja, manchmal wäre es schon nett.> Wenigstens das finally kann man Java als Vorteil zugestehen.
Ich sehe es eher als Krücke, die man eingebaut hat, weil die
Destruktoren fehlen.
Klaus Wachtler schrieb:> ok, daß der Grund für finally in Java ein übler ist, macht> den Mechanismus ja nicht per se schlecht.
Genau darum ging es mir. Wenn man nicht Lust hat, jedes
C-init()-und-end() in eine eigene Klasse zu kapseln (was natürlich sehr
elegant ist), dann vermisst man das finally manchmal schon. Es geht
natürlich auch ohne.
Gruß,
Nico
Klaus Wachtler schrieb:> @Rolf Magnus:> ok, daß der Grund für finally in Java ein übler ist, macht> den Mechanismus ja nicht per se schlecht.
Ganz sicher nicht.
Aber wenn man erst mal auf RAII aufgesprungen ist und das konsequent
macht, will man es nicht mehr missen. Und dann lösen sich 99% aller
finally Probleme, genauso wie 99% aller Memory Leaks in Luft auf und man
vermisst einen GC plötzlich überhaupt nicht mehr :-) Und eigentlich ist
es ja der GC, im Zusammenspiel mit einem unbrauchbaren Destruktor, der
einem Dinge wie finally aufzwingt.
Stimmt, einen GC vermisse ich auch überhaupt nicht. Ist aber immer
schon, wenn einem valgrind zeigt, dass man doch lieber RAII hätte
benutzen sollen, weil man irgendwo ein *_end() vergessen hat.
Rolf Magnus schrieb:> Ich sehe es eher als Krücke, die man eingebaut hat, weil die> Destruktoren fehlen.
Sorry aber das ist ziemlicher Blödsinn.
Mit finally kann man auch innerhalb einer Methode viel feiner granuliert
Dinge 'zum lokalen' Schluß beenden.
Und komm mir nicht mit OO-Designtheorien daß dann das Design falsch ist
und man einzelne Klassen mit jeweils einem Destruktor machen müsste.
Man kann den Spiess auch umdrehen und sagen, C++ hat die Destruktor
Krücke weil ...
Ausserdem wollte ich hier keine "die Programmiersprache ist besser
weil..." Diskussion lostreten.
Aber es fühlen sich manche immer wieder ans Bein gepinkelt, wenn man
"Ihr" System, Sprache, Tool angeblich schlecht macht, nur weil man wagt
darauf hinzuweisen, daß es bei einem anderen Tool, System, Sprache, ...
andere Konzepte gibt.
Viel Spass
U.R. Schmitt schrieb:> Aber es fühlen sich manche immer wieder ans Bein gepinkelt, wenn man> "Ihr" System, Sprache, Tool angeblich schlecht macht, nur weil man wagt> darauf hinzuweisen, daß es bei einem anderen Tool, System, Sprache, ...> andere Konzepte gibt.
Genau darum gehst letztenendes.
In C++ gibt es kein finally, weil man in C++ anders programmiert.
Das hat doch nichts mit 'ans Bein pinkeln zu tun'.
Das muss man den Leuten einfach nur vermitteln, die zb von Java zu C++
kommen. Manche Idiome funktionieren in C++ nun mal anders. Und ehe man
nach Möglichkeiten einer 1:1 Übersetzung sucht, sollte man besser auf
die in der Zielsprache üblichen und bewährten Idiome umschwenken. In C++
ist es nun mal der Destruktor, der Aufräumarbeiten erledigt. Das hat den
Vorteil, dass man als Verwender einer Klasse nicht darauf vergessen kann
die entsprechende Funktion aufzurufen und man sich daher auch nicht
darauf konzentrieren muss.
Um einen deutschen Text ins englische zu übertragen, reicht es auch
nicht aus einfach nur Grammatik und Vokabeln zu beherrschen.
@Detlev T.:
ja, so ähnliche mache ich das auch. Wenn man gute Bezeichner wählt, ist
es übersichtlich und logisch. Evtl. ein klein wenig ineffizienter als
andere Lösungen, aber schön.
@Markus:
>for (i=0; (i<4)&&(!abbruch); i++)
Hmmm, gefällt mir nicht sooo gut. for-Schleifen verwende ich nur, wenn
die Abbruchbedingung feststeht (z.B. Anzahl der Elemente eines Arrays
etc.)
Sonst verwende ich lieber eine while- oder do-while-Schleife.