Ejemplos de buenas goto en C o C++ [cerrado]


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

Resumen (la etiqueta cambió 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 el construcción de lenguaje que causa una rama incondicional. Alternativa depende del uso de estructuras apoyo condicional rama, con un degenerado siempre-cierto condición.
  • La etiqueta documenta la intención sin comentarios adicionales.
  • El lector no tiene que escanear el código interviniente para breaks tempranos (aunque todavía es posible para un hacker sin principios para simular continue con un temprano goto).

Reglas:

  • Fingir que los gotófobos no ganar. Se entiende que lo anterior no se puede usar en código real porque va en contra de lo establecido modismo.
  • Supongamos que todos hemos oído hablar de 'Goto considerado perjudicial' y saber que goto se puede utilizar para escribir código espagueti.
  • Si no está de acuerdo con un ejemplo, criticarlo por mérito técnico solo ('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 terminada ahora. Generó algunas respuestas de alta calidad. Gracias a todos, especialmente aquellos que 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 poner frenos alrededor de la cuerpo de bucle. Observo de pasada que for(;;) y while(true) no te den los frenos de forma gratuita (y omitirlos puede causar errores molestos). De todos modos, no voy a perder más de su poder cerebral en esta bagatela-Puedo vivir con lo inofensivo e 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 tengo que reescribir de otra manera. Por supuesto, puede evitar un goto introduciendo un bucle, una bandera extra, una pila de ifs anidadas, o lo que sea, pero ¿por qué no considerar si goto es tal vez la mejor herramienta para el trabajo? Dicho de otra manera, ¿cuánta fealdad están dispuestos a soportar las personas para evitar el uso de una función de lenguaje incorporado para su propósito previsto? Mi opinión es que incluso añadiendo un la bandera es un precio demasiado alto para pagar. Me gusta que mis variables representen cosas en los dominios del problema o de la solución. '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. IMO, esto hace que el caso más fuerte para un goto de todas las respuestas publicadas, sin duda si lo mides por las contorsiones que un odiador tiene que pasar para evitarlo.

 79
Author: Community, 2008-10-29

16 answers

Heres un truco que he oído de la gente que usa. Aunque nunca lo he visto en la naturaleza. Y solo se aplica a C porque C++ tiene RAII para hacer esto más idiomáticamente.

void foo()
{
    if (!doA())
        goto exit;
    if (!doB())
        goto cleanupA;
    if (!doC())
        goto cleanupB;

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}
 87
Author: Greg Rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-11 04:00:48

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

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

final:

No hay una manera directa de romper los bucles anidados sin un goto.

 85
Author: Paul Nathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:31:43

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 "if the system call was interrupted by a signal, restart it".

 32
Author: fizzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-23 10:57:51

Si el dispositivo de Duff no necesita un goto, ¡entonces 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 Wikipedia entrada .

 14
Author: Mitch Wheat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:08:23

Knuth ha escrito un documento "Programación estructurada con instrucciones GOTO", puede obtenerlo, por ejemplo, desde aquí. Encontrarás muchos ejemplos allí.

 14
Author: zvrba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 07:17:10

Muy frecuentes.

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 para saltar hacia adelante, generalmente fuera de bloques, y nunca dentro de bloques. Esto evita el abuso de do{}while(0) y otras construcciones que aumentan el anidamiento, mientras se mantiene el código legible y estructurado.

 12
Author: ephemient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:18:02

No tengo nada en contra de gotos en general, pero puedo pensar en varias razones por las que no querrías usarlos para un bucle como mencionaste:

  • No limita el alcance, por lo tanto, cualquier variable temporal que use dentro no se liberará hasta más tarde.
  • No limita el alcance, por lo que podría conducir a errores.
  • No limita el ámbito, por lo que no puede reutilizar los mismos nombres de variable más adelante en el código futuro en el mismo ámbito.
  • No limita el alcance, por lo tanto, tiene la posibilidad de saltarse una declaración de variable.
  • La gente no está acostumbrada a ello y hará que tu código sea más difícil de leer.
  • Los bucles anidados de este tipo pueden conducir al código spaghetti, los bucles normales no conducirán al código spaghetti.
 12
Author: Brian R. Bondy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-23 10:57:28

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 el goto con código estructurado y una serie de pruebas, pero creo que esto es más sencillo porque elimina la sangría excesiva:

if (!openDataFile())
  goto quit;

if (!getDataFromFile())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!
 7
Author: Adam Liss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:09:17

@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.

 7
Author: Mitch Wheat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:35:48

A pesar de que he llegado a odiar este patrón con el tiempo, está integrado 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;
}
 6
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 06:49:36

Aquí hay un ejemplo de un buen goto:

// No Code
 5
Author: FlySwat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:01:13

He visto que goto se usa correctamente, pero las situaciones son normalmente feas. Es solo cuando el uso de goto en sí es mucho menos peor que el original. @Johnathon Holland el poblem es tu versión es menos claro. 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 yo prefiero bucles como este, pero algunas personas prefieren while(true).

for (;;)
{
    //code goes here
}
 1
Author: Charles Beattie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-05 19:29:57

Mi queja sobre esto es que se pierde el alcance de bloque; cualquier variable local declarada entre los goto permanece en vigor si el bucle se rompe alguna vez. (Tal vez estás asumiendo que el bucle se ejecuta para siempre; no creo que eso sea lo que el escritor de la pregunta original estaba preguntando, sin embargo.)

El problema del alcance es más un problema con C++, ya que algunos objetos pueden estar dependiendo de que su dtor sea llamado en momentos apropiados.

Para mí, la mejor razón para usar goto es durante un paso múltiple proceso de inicialización 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;
 0
Author: Jim Nelson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:11:44

Yo mismo no uso goto, sin embargo, trabajé con una persona una vez que los usaría en casos específicos. Si no recuerdo mal, su razonamiento estaba en torno a los problemas de rendimiento-también tenía reglas específicas para cómo . Siempre en la misma función, y la etiqueta siempre estaba DEBAJO de la declaración goto.

 0
Author: user25306,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 05:53:48
#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);
}
 0
Author: StrifeSephiroth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-10 12:59:14

@ Greg:

¿Por qué no hacer su ejemplo de esta manera:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
 -1
Author: FlySwat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-29 04:37:36