C++ Direkt bis zu bestimmten Punkt springen?

asdfman schrieb:
"Goto ist verpönt" ist so ein sinnloses Dogma. Sicher kann man damit üblen Scheiß machen, aber gerade für OPs Problem halte ich es für absolut legitim, goto zu benutzen.
Du lieferst Dir da das beste Argument selbst :-) #gotofail ist dir sicher ein Begriff. Das Problem mit goto ist halt, dass du Spaghetti-Code produzierst, der schwierig wartbar und fehleranfällig ist. Sieht Edgar Dijkstra ähnlich: "Go To Statement Considered Harmful".

Den oberen Use-Case kann man beispielsweise so umschreiben:
Code:
char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (p && q && r) {
    /* Do some other work... */
}
free(p);
free(q);
free(r);
Alternativ gibt es dafür die "erweiterten" malloc-Funktionen: mallopt beispielsweise zum automatischen Anspringen einer Fehlerbehandlungsroutine.

Als Faustregel würde ich sagen: goto nicht verwenden, außer man kann sinnvoll begründen, warum es in genau diesem speziellen Fall notwendig ist.
 
Denkst du, ich kenne das Paper nicht? Das ist doch die Bibel aus der das GOTO-Dogma kommt. Ich poste einen viel schöneren und differenzierteren Artikel und den ignorierst du einfach ;(

Your attitude against "goto" is perhaps based upon an excellent but dated
article, "Goto Considered Harmful", written by Edsger W. Dijkstra, and
published by the ACM in 1968. (A recent reprint can be found at
http://www.acm.org/classics/oct95/.) As you can tell from the date, this
article predates modern programming languages and idioms; it comes from a
time when Fortran ruled, and before Fortran 77 provided significant tools
for avoiding spaghetti code.

A "goto" is not, in and of itself, dangerous -- it is a language feature,
one that directly translates to the jump instructions implemented in machine
code. Like pointers, operator overloading, and a host of other "perceived"
evils in programming, "goto" is widely hated by those who've been bitten by
poor programming. Bad code is the product of bad programmers; in my
experience, a poor programmer will write a poor program, regardless of the
availability of "goto."

If you think people can't write spaghetti code in a "goto-less" language, I
can send you some *lovely* examples to disabuse you of that notion. ;)

Used over short distances with well-documented labels, a "goto" can be more
effective, faster, and cleaner than a series of complex flags or other
constructs. The "goto" may also be safer and more intuitive than the
alternative. A "break" is a goto; a "continue" is a "goto" -- these are
statements that move the point of execution explicitly.
 
Zuletzt bearbeitet:
Ich finde, die Kritik ist nicht von der Hand zu weisen. Die Gründe dafür hingegen ('a "goto" can be more
effective, faster, and cleaner') sind fragwürdig, wenn im selben Satz steht 'A "break" is a goto; a "continue" is a "goto"'.
Im Allgemeinen ist es ja auch so, dass (v.a. objektorientierte) Sprachen dieses Sprachfeature meist nicht bieten (Python, Ruby, Java, ...), da es dort Lösungen gibt, das ganze "robuster" umzusetzen. Für imperative Sprachen mag das anders sein.

Außerdem gilt es zu vermeiden, von Velociraptoren gefressen zu werden :-) http://xkcd.com/292/
 
Danke schon mal, hab wieder einiges gelernt.

@ VikingGe
return ist natürlich gut. mit return wird eine Funktion auch immer direkt beendet das hatte ich gar nicht mehr auf dem Schirm... damit bräuchte ich den Kram nur in eine Funktion legen und mit return springe ich raus sobald eine Abfrage das will. Ist wahrscheinlich die schickste Variante und erfüllt den Performanceaspekt.
 
T_55 schrieb:
Der Aspekt der Performance war auch ein Grundgedanke dabei nicht etliche Abfragen zu haben.

Wenn Beispielsweise über 100 Bedingungen abgefragt werden und nur eine ist true dann müssten die Restlichen nicht mehr abgefragt werden und daher wäre ein Sprung schon von Vorteil.

Wenn du in einer if-Abfrage mehrere Bedingungen mit || (oder &&) verknüpfst werden immer nur soviele ausgewertet wie notwendig.

Beispiel:
Code:
int i = 0;
if(true || i++)
	;
std::cout << i << std::endl;
Hier ist die Ausgabe 0, da das zweite Statement garnicht mehr ausgewertet wird (da dies für das Resultat nicht nötig ist).
Code:
int i = 0;
if(false || i++)
	;
std::cout << i << std::endl;
Hier ist die Ausgabe hingegen 1, weil nach dem ersten Statement das Resultat noch nicht feststeht und daher "i++" ausgewertet werden muss.

Du kannst also problemlos 100 Bedingungen in der Form
Code:
if(Bedingung1
   || Bedingung2
   || Bedingung3
   || Bedingung4
   || ... )
	relevanter Code;
std::cout << i << std::endl;
kombinieren, ohne das du dadurch einen Performanceverlust hast.
 
@ Reddy
Das Thema ist m.E. etwas komplexer. Besitzen die Bedingungen keine Nebeneffekte oder Dereferenzieren sie keine Zeiger, so ist es komplett dem Compiler überlassen wie viele Bedinungen er auswerten möchte. Da meines Wissens Sprunginstruktionen nicht ganz "billig" sind, ist es im Fall von wenigen einfachen Bedingungen wahrscheinlich, dass der Compiler zunächst alle Bedingungen auswerten lässt und dann eine Sprunginstruktion einbaut.

Lediglich Bedingungen mit Nebeneffekten oder das Dereferenzieren von Zeigern dürfen nicht mehr ausgeführt werden, sobald das Ergebnis bereits fest steht, bzw. erst ausgewertet werden, wenn das Ergebnis noch nicht fest steht. Dies ist bei Bedingungen mit Nebeneffekten in der Regel vom Programmierer auch so beabsichtigt, und erspart ihm nur die Schreibarbeit von zusätzlichen If-Statements. Ebenso kann sich der Programmierer dadurch Schreibarbeit für zusätzliche Abfragen beim Dereferenzieren von Zeigern sparen, ob der entsprechende Zeiger gültig ist.

Sind die entsprechenden Zeiger jedoch bei einer Bedingungen in einem gegebenen If-Statement immer gültig, so kann dies auch sehr nachteilig sein. Denn der Compiler kann die Speicherzugriffe nun nicht mehr möglichst früh im Programm einbauen und benötigt zusätzliche Sprungbefehle. Ein möglichst frühes Durchführen von Speicherzugriffen ist jedoch oft sehr wichtig für das Latency-Hiding des Prozessors.

Zum Beispiel muss der Compiler aus
Code:
bool* A, *B, *C;
if(*A && *B && *C)
  DoSomething();
folgenden "Code" machen:
Code:
bool* A, *B, *C;
if(*A)
  if(*B)
    if(*C)
       DoSomething();
Er kann es jedoch nicht optimieren zu:
Code:
bool* A, *B, *C;
bool ADRef = *A;
bool BDRef = *B;
bool CDRef = *C;
if(ADRef  && BDRef  && CDRef)
  DoSomething();
Im letzten und optimierten Fall hätten wir jedoch 3 parallele Speicherzugriffe und einen Sprungbefehl während wir im ersten Fall nur einen parallelen Speicherzugriff und 3 Sprungbefehle hätten.
 
Zuletzt bearbeitet:
Zurück
Oben