En este hilo, miramos ejemplos de buenos usos de goto
en C o C ++. Esta inspirado por una respuesta que la gente votó porque pensaron que estaba bromeando.
Resumen (la etiqueta cambió del original para que la intención sea 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 rama incondicional. Las alternativas dependen del uso de estructuras que soporten ramas 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
s (aunque todavía es posible que un pirata informático sin principios simulecontinue
con uno tempranogoto
).
Reglas:
- Finge que los gotophobes no ganaron. Se entiende que lo anterior no se puede usar en código real porque va en contra del lenguaje establecido.
- Supongamos que todos hemos oído hablar de 'Goto considerado dañino' y sabemos que goto se puede usar para escribir código espagueti.
- Si no está de acuerdo con un ejemplo, critíquelo únicamente por sus méritos técnicos ("Porque a la gente no le gusta goto" no es una razón técnica).
Veamos si podemos hablar de esto como adultos.
Editar
Esta pregunta parece haber terminado ahora. Generó algunas respuestas de alta calidad. Gracias a todos, especialmente a aquellos que se tomaron en serio mi pequeño ejemplo de bucle. La mayoría de los escépticos estaban preocupados por la falta de alcance de bloque. Como @quinmars señaló en un comentario, siempre puede colocar llaves alrededor del cuerpo del bucle. Noto de paso eso for(;;)
y while(true)
tampoco le doy las llaves gratis (y omitirlas puede causar errores molestos). De todos modos, no desperdiciaré más 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 goto
como algo que siempre hay que reescribir de otra manera. Por supuesto, puede evitarlo goto
introduciendo un bucle, una bandera adicional, una pila de mensajes de correo if
electrónico anidados o lo que sea, pero ¿por qué no considerar si goto
es quizás la mejor herramienta para el trabajo? Dicho de otra manera, ¿cuánta fealdad están dispuestas a soportar las personas para evitar usar una función de lenguaje incorporada para el propósito previsto? Mi opinión es que incluso agregar una bandera es un precio demasiado alto a pagar. Me gusta que mis variables representen cosas en los dominios de problemas o soluciones. 'Solo para evitar un goto
' no es suficiente.
Aceptaré la primera respuesta que dio el patrón C para ramificar a un bloque de limpieza. En mi opinión, este es el caso más sólido para una goto
de todas las respuestas publicadas, ciertamente si lo mides por las contorsiones que tiene que atravesar un enemigo para evitarlo.
for (;;)
) no tiene puntos de entrada sorprendentes.Respuestas:
Aquí hay un truco que he oído que la gente usa. Sin embargo, nunca lo he 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; }
fuente
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 anidados sin un goto.
fuente
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 bucle degenerado. Esta versión se lee como en inglés "si la llamada al sistema fue interrumpida por una señal, reiníciela".
fuente
continue
es sologoto
una máscara.else break
s (uno para cada if) para que sea equivalente ... qué puedo decir ...goto
o no, tu código es tan bueno como tú :(Si el dispositivo de Duff no necesita un goto, ¡tú tampoco 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 anterior de la entrada de Wikipedia .
fuente
Knuth ha escrito un artículo "Programación estructurada con instrucciones GOTO", puede obtenerlo, por ejemplo, desde aquí . Allí encontrará muchos ejemplos.
fuente
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 utilizo
goto
es para saltar hacia adelante, generalmente fuera de los bloques y nunca dentro de los bloques. Esto evita el abusodo{}while(0)
y otras construcciones que aumentan el anidamiento, mientras se mantiene un código estructurado legible.fuente
do_stuff(thingy) { RAIILockerObject(thingy); ..... /* -->*/ } /* <---*/
:)try { lock();..code.. } finally { unlock(); }
pero sí, C no tiene equivalente.No tengo nada en contra de los gotos en general, pero puedo pensar en varias razones por las que no querrías usarlos para un bucle como el que mencionaste:
fuente
Un buen lugar para usar un goto es 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 sangría excesiva:
fuente
freeResourcesCloseFileQuit
,closeFileQuit
, yquit
.@ fizzer.myopenid.com: su 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.
fuente
Aunque he llegado a odiar este patrón con el tiempo, está arraigado en la programació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; }
fuente
Aquí hay un ejemplo de un buen goto:
// No Code
fuente
He visto que goto se usa correctamente, pero las situaciones suelen ser feas. Es solo cuando el uso de
goto
sí mismo es mucho menos peor que el original. @Johnathon Holland, el problema es que tu versión es menos clara. la gente parece 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 }
fuente
== true
quitó el punto . De todos modos, esto está más cerca de mi estilo real. :)Mi queja sobre esto es que pierde el alcance del bloque; cualquier variable local declarada entre los gotos permanece en vigor si alguna vez se rompe el ciclo. (Tal vez esté asumiendo que el ciclo se ejecuta para siempre; sin embargo, no creo que eso sea lo que estaba haciendo el escritor de la pregunta original).
El problema del ámbito es más un problema con C ++, ya que algunos objetos pueden depender de que se llame a su dtor en los momentos adecuados.
Para mí, la mejor razón para usar goto es durante un proceso de inicialización de varios pasos donde es vital que todos los inits se retiren si uno falla, a 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;
fuente
Yo mismo no uso los goto, sin embargo, una vez trabajé con una persona que los usaría en casos específicos. Si mal no recuerdo, su razón de ser se basaba en problemas de rendimiento; también tenía reglas específicas sobre cómo hacerlo . Siempre en la misma función, y la etiqueta siempre estaba DEBAJO de la instrucción goto.
fuente
#include <stdio.h> #include <string.h> 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); }
fuente
@ Greg:
Por qué no hacer su ejemplo así:
void foo() { if (doA()) { if (doB()) { if (!doC()) { UndoA(); UndoB(); } } else { UndoA(); } } return; }
fuente