Ejemplos de buenos gotos en C o C ++

En este hilo, vemos ejemplos de buenos usos de goto en C o C ++. Está inspirado en una respuesta que la gente votó porque pensaban que estaba bromeando.

Resumen (etiqueta cambiada del original para hacer la intención aún más clara):

 infinite_loop: // code goes here goto infinite_loop; 

Por qué es mejor que las alternativas:

  • Es específico. goto es la construcción del lenguaje que causa una twig incondicional. Las alternativas dependen del uso de estructuras que soporten twigs condicionales, con una condición degenerada siempre verdadera.
  • La etiqueta documenta la intención sin comentarios adicionales.
  • El lector no tiene que escanear el código intermedio para los primeros break (aunque todavía es posible que un hacker sin principios simule continue con un goto temprano).

Reglas:

  • Pretenda que los gotophobes no ganaron. Se entiende que lo anterior no se puede usar en código real porque va en contra del idioma establecido.
  • Supongamos que todos hemos oído hablar de ‘Goto considerado dañino’ y sabemos que goto puede usarse para escribir código de espagueti.
  • Si no está de acuerdo con un ejemplo, critíquelo únicamente por mérito técnico (“porque a la gente no le gusta el goto” no es una razón técnica).

Veamos si podemos hablar de esto como adultos.

Editar

Esta pregunta parece terminada ahora. Generó algunas respuestas de alta calidad. Gracias a todos, especialmente a aquellos que tomaron en serio mi pequeño ejemplo de loop. La mayoría de los escépticos estaban preocupados por la falta de scope del bloque. Como señaló @quinmars en un comentario, siempre puedes poner llaves alrededor del cuerpo del bucle. Noto de paso que for(;;) y while(true) tampoco te dan llaves (y omitirlas puede causar molestos errores). De todos modos, no perderé más de tu poder mental en esta bagatela: puedo vivir con lo inofensivo y lo idiomático for(;;) y while(true) (igual de bien si quiero mantener mi trabajo).

Considerando las otras respuestas, veo que muchas personas ven el goto como algo que siempre debes reescribir de otra manera. Por supuesto, puedes evitar un goto introduciendo un bucle, una bandera extra, una stack de nesteds if , o lo que sea, pero ¿por qué no considerar si goto es quizás la mejor herramienta para el trabajo? Dicho de otra manera, ¿qué fealdad está dispuesta a soportar la gente para evitar el uso de una función de idioma incorporada para su propósito previsto? Mi opinión es que incluso agregar una bandera es un precio demasiado alto para pagar. Me gustan mis variables para representar cosas en el problema o los dominios de la solución. ‘Solo para evitar un goto ‘ no lo corta.

Aceptaré la primera respuesta, que dio el patrón C para la bifurcación a un bloque de limpieza. OMI, este es el caso más fuerte para un goto de todas las respuestas publicadas, ciertamente si lo mides por las contorsiones que un enemigo tiene que pasar para evitarlo.

Este es un truco que escuché de gente usando. Aunque nunca lo había visto en la naturaleza. Y solo se aplica a C porque C ++ tiene RAII para hacer esto de manera más idiomática.

 void foo() { if (!doA()) goto exit; if (!doB()) goto cleanupA; if (!doC()) goto cleanupB; /* everything has succeeded */ return; cleanupB: undoB(); cleanupA: undoA(); exit: return; } 

La necesidad clásica de GOTO en C es la siguiente

 for ... for ... if(breakout_condition) goto final; final: 

No hay una forma sencilla de salir de los bucles nesteds sin un goto.

Aquí está mi ejemplo no tonto, (de Stevens APITUE) para llamadas al sistema Unix que pueden ser interrumpidas por una señal.

 restart: if (system_call() == -1) { if (errno == EINTR) goto restart; // handle real errors } 

La alternativa es un ciclo degenerado. Esta versión se lee en inglés “si la llamada al sistema fue interrumpida por una señal, reiníciela”.

Si el dispositivo de Duff no necesita un goto, ¡tampoco tú deberías! 😉

 void dsend(int count) { int n; if (!count) return; n = (count + 7) / 8; switch (count % 8) { case 0: do { puts("case 0"); case 7: puts("case 7"); case 6: puts("case 6"); case 5: puts("case 5"); case 4: puts("case 4"); case 3: puts("case 3"); case 2: puts("case 2"); case 1: puts("case 1"); } while (--n > 0); } } 

código arriba de la entrada de Wikipedia.

Knuth ha escrito un documento “Progtwigción estructurada con declaraciones GOTO”, puede obtenerlo, por ejemplo, desde aquí . Encontrarás muchos ejemplos allí.

Muy común.

 do_stuff(thingy) { lock(thingy); foo; if (foo failed) { status = -EFOO; goto OUT; } bar; if (bar failed) { status = -EBAR; goto OUT; } do_stuff_to(thingy); OUT: unlock(thingy); return status; } 

El único caso que uso goto es saltar hacia adelante, generalmente fuera de bloques, y nunca en bloques. Esto evita el uso indebido de do{}while(0) y otras construcciones que aumentan la anidación, al tiempo que mantienen un código legible y estructurado.

No tengo nada en contra de los gotos en general, pero puedo pensar en varias razones por las que no querrás usarlos para un ciclo como el que mencionas:

  • No limita el scope, por lo tanto, las variables temporales que utilice en su interior no se liberarán hasta más adelante.
  • No limita el scope, por lo tanto, podría provocar errores.
  • No limita el scope, por lo tanto, no puede volver a utilizar los mismos nombres de variable más adelante en el código futuro en el mismo ámbito.
  • No limita el scope, por lo tanto, tiene la posibilidad de omitir una statement de variable.
  • La gente no está acostumbrada y hará que su código sea más difícil de leer.
  • Los bucles nesteds de este tipo pueden conducir al código de spaghetti, los bucles normales no conducirán al código de spaghetti.

Un buen lugar para usar un goto es en un procedimiento que puede abortar en varios puntos, cada uno de los cuales requiere varios niveles de limpieza. Gotophobes siempre puede reemplazar los gotos con código estructurado y una serie de pruebas, pero creo que esto es más sencillo porque elimina la indentación excesiva:

 if (! openDataFile ())
   goto quit;

 if (! getDataFromFile ())
   goto closeFileAndQuit;

 if (! allocateSomeResources)
   goto freeResourcesAndQuit;

 // Trabaja más aquí ...

 freeResourcesAndQuit:
    // recursos gratuitos
 closeFileAndQuit:
    // cerrar el archivo
 dejar:
    // ¡dejar!

@ fizzer.myopenid.com: el fragmento de código publicado es equivalente a lo siguiente:

  while (system_call() == -1) { if (errno != EINTR) { // handle real errors break; } } 

Definitivamente prefiero esta forma.

Aunque he llegado a odiar este patrón con el tiempo, está integrado en la progtwigción COM.

 #define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error} ... HRESULT SomeMethod(IFoo* pFoo) { HRESULT hr = S_OK; IfFailGo( pFoo->PerformAction() ); IfFailGo( pFoo->SomeOtherAction() ); Error: return hr; } 

Aquí hay un ejemplo de un buen goto:

 // No Code 

He visto goto usado correctamente, pero las situaciones son normalmente feas. Es solo cuando el uso de goto es mucho peor que el original. @Johnathon Holland, el problema es que tu versión es menos clara. las personas parecen tener miedo de las variables locales:

 void foo() { bool doAsuccess = doA(); bool doBsuccess = doAsuccess && doB(); bool doCsuccess = doBsuccess && doC(); if (!doCsuccess) { if (doBsuccess) undoB(); if (doAsuccess) undoA(); } } 

Y prefiero bucles como este, pero algunas personas prefieren while(true) .

 for (;;) { //code goes here } 

Mi queja sobre esto es que pierdes el scope del bloque; cualquier variable local declarada entre los gotos permanece vigente si el bucle se rompe alguna vez. (Tal vez suponga que el ciclo se ejecuta para siempre, pero no creo que eso fuera lo que el autor de la pregunta original estaba formulando).

El problema del scope es más un problema con C ++, ya que algunos objetos pueden ser dependientes de que se llame a su controlador en los momentos apropiados.

Para mí, la mejor razón para usar goto es durante un proceso de inicialización de varios pasos donde es vital que todas las entradas sean restituidas si falla, a la la:

 if(!foo_init()) goto bye; if(!bar_init()) goto foo_bye; if(!xyzzy_init()) goto bar_bye; return TRUE; bar_bye: bar_terminate(); foo_bye: foo_terminate(); bye: return FALSE; 

No uso goto yo mismo, sin embargo, sí trabajé con una persona una vez que los usaría en casos específicos. Si mal no recuerdo, su razón de ser era sobre problemas de rendimiento, también tenía reglas específicas de cómo hacerlo . Siempre en la misma función, y la etiqueta siempre estaba ABAJO de la statement goto.

 #include  #include  int main() { char name[64]; char url[80]; /*The final url name with http://www..com*/ char *pName; int x; pName = name; INPUT: printf("\nWrite the name of a web page (Without www, http, .com) "); gets(name); for(x=0;x< =(strlen(name));x++) if(*(pName+0) == '\0' || *(pName+x) == ' ') { printf("Name blank or with spaces!"); getch(); system("cls"); goto INPUT; } strcpy(url,"http://www."); strcat(url,name); strcat(url,".com"); printf("%s",url); return(0); } 

@Greg:

Por qué no hacer tu ejemplo así:

 void foo() { if (doA()) { if (doB()) { if (!doC()) { UndoA(); UndoB(); } } else { UndoA(); } } return; }