C schleifen und sprungmarken?

Amarok2 schrieb:
ja ich brauch goto nur für 2 sachen... das sollte ja dann noch lesbar sein ;)

wie geht das, wenn ich 2 verschachtelte while abfragen hab, das ich da aus beiden rausspringe?
beispiel:

Code:
while(1)
{
   while(1)
   {
      if(bedingung == true)
      {
         goto label;
      }
   }
}
label:

also wie schreib ich das ohne goto?

Ganz einfach, du packst das ganze Schleifenkonstrukt in eine neue Funktion und springst dann einfach mit einem simplen return aus der ganzen Funktion raus. Nehmen wir mal an, dein jetziger Code sieht so aus:

Code:
void blabla()
{
	// ...
	// ...

	while(1)
	{
		while(1)
		{
			if(bedingung == true)
			{
				goto label;
			}
		}
	}
	
label:
	// weiterer Code hier
	// ...
	// ...
}

Mein Vorschlag wäre stattdessen:


Code:
void schleifenFunktion( /* alle benötigten Argumente hier ... */ )
{
	while(1)
	{
		while(1)
		{
			if(bedingung == true)
			{
				//goto label; <-- Pfui! ;P
				return; // Verlasse Funktion und damit auch beide Schleifen.
			}
		}
	}
}


void blabla()
{
	// ...
	// ...

	schleifenFunktion( /* Übergabeparameter */ );
	
//label: <-- Brauchen wir jetzt nicht mehr.
	// weiterer Code hier
	// ...
	// ...
}
Ergänzung ()

dammi schrieb:
Entweder mit Break oder Exit. ist so ziemlich gleich in allen Sprachen.

Ein exit wäre wohl ein bißchen übertrieben, nur um aus einer Schleife rauszuspringen, oder? ;)
 
format(C) && endAllLifeNow() && destroyUniverse();

// Warning: dead code here
...

:D
 
ich bezweifle, dass destroyUniverse() in der lage sein wird noch ein return raus zuhauen ;), ergo wäre eine Verknüpfung per && nicht möglich...

Zu der Frage der Geschwindigkeit, bau einfach mal einen simplen Zähler und lass ihn mal ne Sekunde laufen, da wird einem schnell klar auch die kleinsten Mikroprozessoren sind verdammt flink.

Der eigentliche Grund wieso goto in Verruf gerate ist, ist der ominöse Spaghetticode. Wenn du das Programm nur für dich haben möchtest, kannst du es ruhig nutzen. Sollen aber auch andere mit dem Code arbeiten, verzichte lieber drauf, wobei auch hier die Sache Diskussionsgrundlage ist (siehe Linux-Kernel-Mailing-Liste). Aber im allgemein ist der Verzicht von unüberschaubarem Code anzuraten ;)
 
M0rpHeU5 schrieb:
http://home.fhtw-berlin.de/~junghans/cref/SYNTAX/goto.html

GoTo sollte vermieden werden, aber in aktuellen Software Architektur Vorträgen wird es wieder als wieder geduldet / erlaubt eingestuft, wenn es die Lesbarkeit des Codes im Vergleich zu den anderen Kontrollstrukturen erheblich erleichert;

Ähm die Quelle die du da angibst ist aber echt interessant. Prof. Junghans haut dir Code mit sprunganweisungen um die Ohren.
 
Was findet ihr sinnvoller?
Code:
int doit(void) {
  int a,b,c,d,e;

  if((a = malloc(sizeof(int))) == NULL) return 0;
  if((b = malloc(sizeof(int))) == NULL) {
    free(a);
    return 0;
  }
  if((c = malloc(sizeof(int))) == NULL) {
    free(b);
    free(a);
    return 0;
  }
  if((d = malloc(sizeof(int))) == NULL) {
    free(c);
    free(b);
    free(a);
    return 0;
  }
  if((e = malloc(sizeof(int))) == NULL) {
    free(d);
    free(c);
    free(b);
    free(a);
    return 0;
  }

  [...]
  free(e);
  free(d);
  free(c);
  free(b);
  free(a);
  return 1;
}
Oder doch eher
Code:
int doit(void) {
  int a,b,c,d,e, ret = 0;

  if((a = malloc(sizeof(int))) == NULL) return ret;
  if((b = malloc(sizeof(int))) == NULL) goto freea;
  if((c = malloc(sizeof(int))) == NULL) goto freeb;
  if((d = malloc(sizeof(int))) == NULL) goto freec;
  if((e = malloc(sizeof(int))) == NULL) goto freed;

  [...]

  ret = 1;
  free(e);
freed:
  free(d);
freec:
  free(c);
freeb:
  free(b);
freea:
  free(a);
  return ret;
}

Finde letzteres deutlich besser zu lesen, weil es ständige Wiederholung vermeidet
und man sich auf das Wesentliche konzentrieren kann. Es gibt auch keinen Spaghetti-
code trotz goto, denn der Lesefluss geht weiterhin von oben nach unten.

Sicher ist goto in den allermeisten Fällen überflüssig geworden, aber es grundsätzlich
zu verteufeln ist dummes Nachgeplapper und stumpfer Dogmatismus, der es in genug
Fällen nur schlimmer macht.

€: Ein Beispiel aus einem meiner Projekte:
Code:
static int rsa_storekey(rsa_keypair_t *keypair, mp_int *p,
        mp_int *q, mp_int *m, mp_int *e, mp_int *d) {
    rsa_pubkey_t *pub = &keypair->pub;
    rsa_privkey_t *priv = &keypair->priv;
    mp_int p1, q1;
    int ret;

    if((ret = mp_init_copy(&pub->e, e)) != MP_OKAY) return ret;
    if((ret = mp_init_copy(&pub->m, m)) != MP_OKAY) goto cleare;
    if((ret = mp_init_copy(&priv->p, p)) != MP_OKAY) goto clearm;
    if((ret = mp_init_copy(&priv->q, q)) != MP_OKAY) goto clearp;
    if((ret = mp_init_multi(&priv->dp, &priv->dq, &priv->qinv, &p1, &q1, NULL)) != MP_OKAY) goto clearq;

    if((ret = mp_invmod(&priv->q, &priv->p, &priv->qinv)) != MP_OKAY) goto clear;
    if((ret = mp_sub1(p, &p1)) != MP_OKAY) goto clear;
    if((ret = mp_sub1(q, &q1)) != MP_OKAY) goto clear;
    if((ret = mp_mod(d, &p1, &priv->dp)) != MP_OKAY) goto clear;
    if((ret = mp_mod(d, &q1, &priv->dq)) != MP_OKAY) goto clear;

    mp_clear_multi(&p1, &q1, NULL);
    return MP_OKAY;

clear:
    mp_clear_multi(&p1, &q1, NULL);
    mp_clear_multi(&priv->dp, &priv->dq, priv->qinv, NULL);
clearq:
    mp_clear(&priv->q);
clearp:
    mp_clear(&priv->p);
clearm:
    mp_clear(&pub->m);
cleare:
    mp_clear(&pub->e);
    return ret;
}

[...]

int rsa_decrypt(mp_int *ct, rsa_privkey_t *priv, mp_int *msg) {
    mp_int m1, m2;
    int ret;

    if((ret = mp_init_multi(&m1, &m2, NULL)) != MP_OKAY)    return ret;
    if((ret = mp_exptmod(ct, &priv->dp, &priv->p, &m1)) != MP_OKAY) goto dec_end;
    if((ret = mp_exptmod(ct, &priv->dq, &priv->q, &m2)) != MP_OKAY) goto dec_end;
    if((ret = mp_sub(&m1, &m2, &m1)) != MP_OKAY)            goto dec_end;
    if((ret = mp_add(&m1, &priv->p, &m1)) != MP_OKAY)       goto dec_end;
    if((ret = mp_mul(&priv->qinv, &m1, &m1)) != MP_OKAY)    goto dec_end;
    if((ret = mp_mod(&m1, &priv->p, &m1)) != MP_OKAY)       goto dec_end;
    if((ret = mp_mul(&priv->q, &m1, &m1)) != MP_OKAY)       goto dec_end;
    if((ret = mp_add(&m1, &m2, msg)) != MP_OKAY)            goto dec_end;

dec_end:
    mp_clear_multi(&m1, &m2, NULL);
    return ret;
}

Ohne gotos wären die beiden Funktionen der Krebs auf Rädern. Fast nur Codeschnipsel
die sich wiederholen und damit den Leser ermüden und vom Wesentlichen ablenken. Ist
es da wert, stumpf auf goto zu verzichten, nur weil die Leute auf Computerbase.de mal
gehört haben, dass jemand (missbräuchliche) Benutzung der Sprunganweisung für
"considered harmful" einstuft? Ich denke nicht. Sinnvoll eingesetzt verbessert ein goto
klar die Lesbarkeit und man sollte es dann auch verwenden.
 
Zuletzt bearbeitet:
In C wäre goto in so einem Fall wohl tatsächlich noch eine der besseren Lösungen. :-\ Aber in C++ .... ja ja ok, ich halt's Maul ... ;)
 
asdfman schrieb:
Was findet ihr sinnvoller?

Keines von beiden, sondern ein 5 Elemente großes Array und eine Schleife die hochzählt, auf null prüft und bis eines null ist freigibt.

Ist kürzer und übersichtlicher als beides.

Aber ich gebe Dir recht, für bestimmte Anwendungsgebiete hat es seine Berechtigung, speziell wenn man wirklich um jede Anweisung wegen der Performance kämpft, die vorgeschlagene Schleife ist natürlich rechenintensiver.

Dennoch sehe ich das beim Thema des Threadstarters nicht gegeben, hier wäre es einfach nur unsauber.

Viele Grüße
Winni
 
Mit so einer Aufräumschleife kommst du aber auch nur dann auf einen grünen Zweig, wenn es sich bei den aufzuräumenden Sachen um homogene Variablen handelt. Wenn du zum Beispeil einen Pointer auf ein gemalloctes Struct, ein HWND auf irgend ein Windows-API-Objekt und ein anderweitiges Resourcenhandle hast, mußt du doch wieder in den sauren Apfel beißen.
 
@asdfman: oder man löst es in diese Richtung:

Klar, wenn dann Anweisungen zwischen dein einzelnen mallocs sein sollen, wirds wieder ein mehr oder weniger ungehbarer Weg. Das Beispiel zeigt eigentlich weniger den Nutzen von GoTo als viel mehr die Notwendigkeit von Exception-Handling, denn robusten Code zu schreiben ohne Exception-Handling kann ganz schön ausarten...

Code:
int doit(void) {
  int a = NULL, b = NULL, c = NULL, d = NULL, e = NULL, ret = 0;
 
  if ((a = malloc(sizeof(int))) != NULL) &&
      (b = malloc(sizeof(int))) != NULL) &&
      (c = malloc(sizeof(int))) != NULL) &&
      (d = malloc(sizeof(int))) != NULL) &&
      (e = malloc(sizeof(int))) != NULL)) {
 
    [...]
 
    ret = 1;
  }
  
  if (a <> NULL) free(a);
  if (b <> NULL) free(b);
  if (c <> NULL) free(c);
  if (d <> NULL) free(d);
  if (e <> NULL) free(e);

  return ret;
}
 
Zuletzt bearbeitet:
@1668mib
free darf man auch mit nem Null-Pointer aufrufen, dann passiert genau garnix.
(ich nehme mal an, das sollen alles int* sein?)
 
@7H3 N4C3R: Hab nur den Code von asdfman umgeschrieben, also sollten int* gemeint sein...
 
Zurück
Oben