Pregunta

Todo el mundo conoce la situación de Dijkstra. Cartas al editor:ir a declaración considerada dañina (también aquí transcripción .html y aquí .pdf) y desde entonces ha habido un impulso formidable para evitar la declaración goto siempre que sea posible.Si bien es posible utilizar goto para producir código extenso y que no se puede mantener, permanece en lenguajes de programación modernos.Incluso los avanzados continuación La estructura de control en Scheme se puede describir como un goto sofisticado.

¿Qué circunstancias justifican el uso de goto?¿Cuándo es mejor evitarlo?

Como pregunta de seguimiento:C proporciona un par de funciones, setjmp y longjmp, que brindan la capacidad de ir no solo dentro del marco de pila actual sino también dentro de cualquiera de los marcos de llamada.¿Deberían considerarse tan peligrosos como goto?¿Más peligroso?


El propio Dijkstra lamentó aquel título, del que no era responsable.Al final de EWD1308 (también aquí .pdf) escribió:

Por fin una breve historia para que conste.En 1968, las comunicaciones de la ACM publicaron un texto mío bajo el título "La declaración de goto considerada dañina", que en los últimos años se haría referencia con mayor frecuencia, lamentablemente, a menudo por autores que no habían visto más que su título, que se convirtió en una piedra angular de mi fama al convertirse en una plantilla:Veríamos todo tipo de artículos bajo el título "X considerado dañino" para casi cualquier x, incluida una titulada "Dijkstra considerada dañina".¿Pero qué había pasado?Había enviado un documento bajo el título "Un caso contra la declaración de goto", que, para acelerar su publicación, el editor se había convertido en una" carta al editor ", y en el proceso le había dado un nuevo título de su propia invención!El editor fue Niklaus Wirth.

Un artículo clásico bien pensado sobre este tema, comparable al de Dijkstra, es Programación estructurada con ir a declaraciones, por Donald E.Knuth.Leer ambos ayuda a restablecer el contexto y una comprensión no dogmática del tema.En este artículo se recoge la opinión de Dijkstra sobre este caso y es aún más contundente:

Donald E.Knuth: Creo que al presentar tal punto de vista, de hecho no estoy en desacuerdo con las ideas de Dijkstra, ya que recientemente escribió lo siguiente:"Por favor, no caigas en la trampa de creer que soy terriblemente dogmático sobre [la declaración ir a la declaración]. Tengo la sensación incómoda de que otros están haciendo una religión con ella, como si los problemas conceptuales de programación pudieran resolverse mediante un solo truco, por una forma simple de disciplina de codificación."

¿Fue útil?

Solución

Las siguientes afirmaciones son generalizaciones;Si bien siempre es posible alegar una excepción, por lo general (en mi experiencia y humilde opinión) no vale la pena correr riesgos.

  1. El uso ilimitado de direcciones de memoria (ya sea GOTO o punteros sin formato) brinda demasiadas oportunidades para cometer errores fácilmente evitables.
  2. Cuantas más formas haya de llegar a una "ubicación" particular en el código, menos confianza se podrá tener sobre cuál es el estado del sistema en ese punto.(Vea abajo.)
  3. La programación estructurada en mi humilde opinión se trata menos de "evitar GOTO" y más de hacer que la estructura del código coincida con la estructura de los datos.Por ejemplo, una estructura de datos repetida (p. ej.matriz, archivo secuencial, etc.) se procesa naturalmente mediante una unidad de código repetida.Tener estructuras integradas (p. ej.mientras, para, hasta, para cada uno, etc.) permite al programador evitar el tedio de repetir los mismos patrones de código cliché.
  4. Incluso si GOTO es un detalle de implementación de bajo nivel (¡no siempre es el caso!), está por debajo del nivel en el que el programador debería estar pensando.¿Cuántos programadores equilibran sus chequeras personales en binario sin formato?¿Cuántos programadores se preocupan por qué sector del disco contiene un registro particular, en lugar de simplemente proporcionar una clave para un motor de base de datos (y de cuántas maneras podrían salir mal las cosas si realmente escribiéramos todos nuestros programas en términos de sectores físicos del disco)?

Notas a pie de página de lo anterior:

Respecto al punto 2, considere el siguiente código:

a = b + 1
/* do something with a */

En el punto "hacer algo" del código, podemos afirmar con alta confianza que a es mayor que b.(Sí, estoy ignorando la posibilidad de que se produzca un desbordamiento de enteros no atrapado.No nos empantanemos en un ejemplo simple.)

Por otro lado, si el código se hubiera leído de esta manera:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

La multiplicidad de formas de llegar a la etiqueta 10 significa que tenemos que trabajar mucho más duro para tener confianza en las relaciones entre a y b en ese punto.(¡De hecho, en el caso general es indecidible!)

Con respecto al punto 4, toda la noción de "ir a algún lugar" en el código es sólo una metáfora.En realidad, nada "va" a ninguna parte dentro de la CPU, excepto electrones y fotones (para el calor residual).A veces renunciamos a una metáfora por otra más útil.Recuerdo haber encontrado (¡hace unas décadas!) un lenguaje en el que

if (some condition) {
  action-1
} else {
  action-2
}

se implementó en una máquina virtual compilando la acción-1 y la acción-2 como rutinas sin parámetros fuera de línea, luego usando un único código de operación de VM de dos argumentos que usaba el valor booleano de la condición para invocar uno u otro.El concepto era simplemente "elegir qué invocar ahora" en lugar de "ir aquí o ir allí".De nuevo, sólo un cambio de metáfora.

Otros consejos

XKCD's GOTO Comic

Un compañero de trabajo mío dijo que la única razón para usar GOTO es si te programaste tan en un rincón que es la única salida.En otras palabras, diseñe correctamente con anticipación y no necesitará usar GOTO más adelante.

Pensé que este cómic ilustra que "podría reestructurar el flujo del programa, o usar un pequeño 'goto' en su lugar". Un GoTo es una salida débil cuando tienes un diseño débil. Los velociraptores se aprovechan de los débiles.

A veces es válido utilizar GOTO como alternativa al manejo de excepciones dentro de una sola función:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

El código COM parece caer en este patrón con bastante frecuencia.

Solo recuerdo haber usado goto una vez.Tenía una serie de cinco bucles contados anidados y necesitaba poder salir temprano de toda la estructura desde el interior según ciertas condiciones:

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

Podría haber declarado fácilmente una variable de interrupción booleana y usarla como parte del condicional para cada bucle, pero en este caso decidí que GOTO era igual de práctico y legible.

Ningún velociraptor me atacó.

ya teniamos esto discusión y estoy a la espera mi punto.

Además, estoy harto de que la gente describa las estructuras lingüísticas de nivel superior como "goto disfrazados” porque claramente no entienden el punto. en absoluto.Por ejemplo:

Incluso la estructura avanzada de control de continuación en Scheme puede describirse como un goto sofisticado.

Eso es una completa tontería. Cada La estructura de control se puede implementar en términos de goto pero esta observación es completamente trivial e inútil. goto no se considera perjudicial por sus efectos positivos sino por sus consecuencias negativas y estas han sido eliminadas mediante programación estructurada.

De manera similar, decir “GOTO es una herramienta y, como todas las herramientas, se puede usar y abusar de ella” está completamente fuera de lugar.Ningún trabajador de la construcción moderno usaría una roca y afirmaría que "es una herramienta". Las rocas han sido reemplazadas por martillos. goto ha sido reemplazada por estructuras de control.Si el trabajador de la construcción se quedara abandonado en la naturaleza sin un martillo, por supuesto usaría una piedra en su lugar.Si un programador tiene que usar un lenguaje de programación inferior que no tiene la característica X, bueno, por supuesto, puede que tenga que usar goto en cambio.Pero si lo usa en cualquier otro lugar en lugar de la característica apropiada del lenguaje, claramente no ha entendido el lenguaje correctamente y lo usa incorrectamente.Es realmente tan simple como eso.

Goto está muy abajo en mi lista de cosas para incluir en un programa por el simple hecho de hacerlo.Eso no significa que sea inaceptable.

Goto puede ser bueno para las máquinas de estado.Una declaración de cambio en un bucle es (en orden de importancia típica):(a) en realidad no representativo del flujo de control, (b) feo, (c) potencialmente ineficiente según el lenguaje y el compilador.Entonces termina escribiendo una función por estado y haciendo cosas como "return next_state"; que incluso se parecen a Goto.

Por supuesto, es difícil codificar máquinas de estados de una manera que las haga fáciles de entender.Sin embargo, ninguna de esas dificultades tiene que ver con el uso de goto, y nada de eso puede reducirse mediante el uso de estructuras de control alternativas.A menos que su idioma tenga una construcción de "máquina de estados".El mío no.

En esas raras ocasiones en las que su algoritmo es realmente más comprensible en términos de una ruta a través de una secuencia de nodos (estados) conectados por un conjunto limitado de transiciones permisibles (gotos), en lugar de por cualquier flujo de control más específico (bucles, condicionales, todo eso). ), entonces eso debería estar explícito en el código.Y deberías dibujar un bonito diagrama.

setjmp/longjmp puede ser bueno para implementar excepciones o comportamientos similares a excepciones.Si bien no son universalmente elogiadas, las excepciones generalmente se consideran una estructura de control "válida".

setjmp/longjmp son "más peligrosos" que goto en el sentido de que son más difíciles de usar correctamente, y mucho menos de manera comprensible.

Nunca ha habido, ni habrá, ningún idioma en el que sea el mínimo difícil escribir un código incorrecto.--Donald Knuth.

Quitar goto de C no facilitaría la escritura de un buen código en C.De hecho, preferiría pasar por alto el punto de que C es supuesto ser capaz de actuar como un lenguaje ensamblador glorificado.

Lo siguiente será "indicadores considerados dañinos", luego "escribir como pato considerado dañino".Entonces, ¿quién quedará para defenderte cuando vengan a quitarte tu construcción de programación insegura?¿Eh?

En Linux:Usando goto en código kernel En Kernel Trap, hay una discusión con Linus Torvalds y un "chico nuevo" sobre el uso de GOTO en el código de Linux.Hay algunos puntos muy buenos allí y Linus se vistió con esa arrogancia habitual :)

Algunos pasajes:

Lino:"No, las personas de CS te han lavado el cerebro que pensaron que Niklaus Wirth realmente sabía de lo que estaba hablando.No lo hizo.No tiene una pista de maldito ".

-

Lino:"Creo que los GOTO están bien, y a menudo son más legibles que grandes cantidades de sangría".

-

Lino:"Por supuesto, en idiomas estúpidos como Pascal, donde las etiquetas no pueden ser descriptivas, los GoTo pueden ser malos".

Cª, goto solo funciona dentro del alcance de la función actual, que tiende a localizar cualquier error potencial. setjmp y longjmp son mucho más peligrosos, ya que no son locales, son complicados y dependen de la implementación.Sin embargo, en la práctica son demasiado oscuros y poco comunes como para causar muchos problemas.

Creo que el peligro de goto en C es muy exagerado.Recuerda que el original goto Las discusiones tenían lugar en la época de lenguajes como el antiguo BASIC, donde los principiantes escribían código espagueti como este:

3420 IF A > 2 THEN GOTO 1430

Aquí Linus describe un uso apropiado de goto: http://www.kernel.org/doc/Documentation/CodingStyle (Capítulo 7).

Hoy en día, es difícil ver la importancia de la GOTO declaración porque la gente de "programación estructurada" ganó en su mayoría el debate y los lenguajes actuales tienen suficientes estructuras de flujo de control para evitar GOTO.

Cuente el número de gotos en un programa C moderno.Ahora suma el número de break, continue, y return declaraciones.Además, suma el número de veces que utilizas if, else, while, switch o case.Eso es aproximadamente cuantos GOTOs que habría tenido su programa si estuviera escribiendo en FORTRAN o BASIC en 1968 cuando Dijkstra escribió su carta.

Los lenguajes de programación de la época carecían de flujo de control.Por ejemplo, en el Dartmouth BASIC original:

  • IF las declaraciones no tenían ELSE.Si querías uno tenías que escribir:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • Incluso si tu IF La declaración no necesitaba una ELSE, todavía estaba limitado a una sola línea, que generalmente consistía en una GOTO.

  • no hubo DO...LOOP declaración.Para no-FOR bucles, tenías que finalizar el bucle con un explícito GOTO o IF...GOTO De regreso al principio.

  • no hubo SELECT CASE.Tuviste que usar ON...GOTO.

Entonces, terminaste con un lote de GOTOs en su programa.Y no podías depender de la restricción de GOTOs dentro de una sola subrutina (porque GOSUB...RETURN era un concepto tan débil de subrutinas), por lo que estos GOTOPodríamos ir en cualquier lugar.Obviamente, esto hizo que el flujo de control fuera difícil de seguir.

Aquí es donde el anti-GOTO vino el movimiento.

Ir a puede proporcionar una especie de sustituto para el manejo de excepciones "real" en ciertos casos.Considerar:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

Obviamente, este código se simplificó para ocupar menos espacio, así que no te obsesiones demasiado con los detalles.Pero considere una alternativa que he visto demasiadas veces en producción código de codificadores que llegan a extremos absurdos para evitar el uso de goto:

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

Ahora, funcionalmente, este código hace exactamente lo mismo.De hecho, el código generado por el compilador es casi idéntico.Sin embargo, en el afán del programador por apaciguar Nogoto (el temido dios de la reprimenda académica), este programador ha roto por completo el idioma subyacente de que el while El bucle representa e hizo un número real sobre la legibilidad del código. Esto no es mejor.

Entonces, la moraleja de la historia es que, si te encuentras recurriendo a algo realmente estúpido para evitar usar goto, entonces no lo hagas.

Donald E.Knuth respondió a esta pregunta en el libro "Literate Programming", 1992 CSLI.En P.17 hay un ensayo "Programación estructurada con declaraciones goto" (PDF).Creo que el artículo también podría haberse publicado en otros libros.

El artículo describe la sugerencia de Dijkstra y describe las circunstancias en las que es válida.Pero también ofrece una serie de contraejemplos (problemas y algoritmos) que no pueden reproducirse fácilmente utilizando únicamente bucles estructurados.

El artículo contiene una descripción completa del problema, la historia, ejemplos y contraejemplos.

Atraído por Jay Ballou al agregar una respuesta, agregaré mis £0,02.Si Bruno Ranschaert no lo hubiera hecho ya, habría mencionado el artículo "Programación estructurada con declaraciones GOTO" de Knuth.

Una cosa que no he visto discutida es el tipo de código que, aunque no es exactamente común, se enseña en los libros de texto de Fortran.Cosas como el rango extendido de un bucle DO y subrutinas de código abierto (recuerde, esto sería Fortran II, Fortran IV o Fortran 66, no Fortran 77 o 90).Existe al menos la posibilidad de que los detalles sintácticos sean inexactos, pero los conceptos deben ser lo suficientemente precisos.Los fragmentos en cada caso están dentro de una única función.

Tenga en cuenta que el libro excelente pero anticuado (y agotado) 'Los elementos del estilo de programación, 2.ª edición' de Kernighan & Plauger incluye algunos ejemplos de la vida real de abuso de GOTO en libros de texto de programación de su época (finales de los 70).Sin embargo, el material que aparece a continuación no pertenece a ese libro.

Rango extendido para un bucle DO

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

Una de las razones de tales tonterías fue la antigua tarjeta perforada.Podrías notar que las etiquetas (¡muy fuera de secuencia porque era un estilo canónico!) están en la columna 1 (en realidad, tenían que estar en las columnas 1 a 5) y el código está en las columnas 7 a 72 (la columna 6 era la continuación columna de marcador).A las columnas 73 a 80 se les asignaba un número de secuencia y había máquinas que clasificaban las barajas de tarjetas perforadas en orden de número de secuencia.Si tuviera su programa en tarjetas secuenciadas y necesitara agregar algunas tarjetas (líneas) en medio de un bucle, tendría que volver a escribir todo después de esas líneas adicionales.Sin embargo, si reemplazas una tarjeta con el elemento GOTO, puedes evitar volver a secuenciar todas las tarjetas: simplemente colocas las nuevas tarjetas al final de la rutina con nuevos números de secuencia.Considérelo como el primer intento de 'computación verde': un ahorro de tarjetas perforadas (o, más específicamente, un ahorro de mano de obra de remecanografiado, y un ahorro de los consiguientes errores de recodificación).

Oh, también puedes notar que estoy haciendo trampa y no gritando: Fortran IV normalmente estaba escrito en mayúsculas.

Subrutina de código abierto

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

El GOTO entre las etiquetas 76 y 54 es una versión del goto calculado.Si la variable i tiene el valor 1, vaya a la primera etiqueta de la lista (123);si tiene el valor 2, pase al segundo, y así sucesivamente.El fragmento de 76 al punto goto calculado es la subrutina de código abierto.Era un fragmento de código ejecutado como una subrutina, pero escrito en el cuerpo de una función.(Fortran también tenía funciones de declaración, que eran funciones integradas que encajaban en una sola línea).

Había peores construcciones que el goto calculado: se podían asignar etiquetas a las variables y luego usar un goto asignado.googlear asignado ir a me dice que fue eliminado de Fortran 95.Apunte uno a la revolución de la programación estructurada que podría decirse que comenzó en público con la carta o el artículo "GOTO considerado perjudicial" de Dijkstra.

Sin algún conocimiento del tipo de cosas que se hacían en Fortran (y en otros idiomas, la mayoría de los cuales, con razón, han quedado en el camino), es difícil para nosotros, los recién llegados, comprender el alcance del problema con el que se enfrentaba Dijkstra.Diablos, no comencé a programar hasta diez años después de que se publicó esa carta (pero tuve la mala suerte de programar en Fortran IV por un tiempo).

Ir a considerado útil.

Empecé a programar en 1975.Para los programadores de la década de 1970, las palabras "goto se considera dañino" decían más o menos que valía la pena probar nuevos lenguajes de programación con estructuras de control modernas.Probamos los nuevos idiomas.Nos convertimos rápidamente.Nunca volvimos.

Nunca volvimos, pero, si eres más joven, entonces nunca has estado allí.

Ahora bien, tener experiencia en lenguajes de programación antiguos puede no ser muy útil excepto como indicador de la edad del programador.Sin embargo, los programadores más jóvenes carecen de esta formación, por lo que ya no entienden el mensaje que transmitía el eslogan "goto considerado perjudicial". a su público objetivo en el momento de su presentación.

Los lemas que uno no comprende no son muy esclarecedores.Probablemente sea mejor olvidar esos eslóganes.Este tipo de consignas no ayudan.

Sin embargo, este eslogan en particular, "Goto se considera dañino", ha adquirido vida propia de no-muerto.

¿No se puede abusar de goto?Respuesta:claro, pero ¿y qué?Prácticamente todos los elementos de programación. poder ser abusado.los humildes bool por ejemplo, se abusa de él con más frecuencia de lo que a algunos de nosotros nos gustaría creer.

Por el contrario, no recuerdo haber conocido ningún caso real de abuso de goto desde 1990.

El mayor problema con goto probablemente no sea técnico sino social.Los programadores que no saben mucho a veces parecen sentir que desaprobar goto los hace parecer inteligentes.Es posible que deba satisfacer a dichos programadores de vez en cuando.Así es la vida.

Lo peor de goto hoy en día es que no se utiliza lo suficiente.

No existen cosas como GOTO considerado perjudicial.

GOTO es una herramienta y, como todas las herramientas, se puede utilizar y abusado.

Sin embargo, hay muchas herramientas en el mundo de la programación que tienden a ser abusado más que ser usado, y GOTO es uno de ellos.el CON La afirmación de Delfos es otra.

Personalmente tampoco uso en código típico, pero he tenido el uso extraño de ambos IR A y CON que estaban justificados y una solución alternativa habría contenido más código.

La mejor solución sería que el compilador simplemente le advirtiera que la palabra clave era contaminado, y tendrías que incluir un par de directivas pragmáticas alrededor de la declaración para eliminar las advertencias.

Es como decirle a tus hijos que no correr con tijeras.Las tijeras no son malas, pero su uso quizás no sea la mejor manera de mantener la salud.

Desde que comencé a hacer algunas cosas en el kernel de Linux, los gotos ya no me molestan tanto como antes.Al principio me horroricé un poco al ver que ellos (los chicos del kernel) agregaron gotos a mi código.Desde entonces me he acostumbrado al uso de gotos, en algunos contextos limitados, y ahora yo mismo los usaré ocasionalmente.Normalmente, es un goto que salta al final de una función para realizar algún tipo de limpieza y rescate, en lugar de duplicar esa misma limpieza y rescate en varios lugares de la función.Y, por lo general, no es algo lo suficientemente grande como para transferirlo a otra función, por ejemplo.liberar algunas variables mal asignadas localmente (k) es un caso típico.

Escribí código que usó setjmp/longjmp solo una vez.Estaba en un programa secuenciador de batería MIDI.La reproducción se produjo en un proceso separado de toda la interacción del usuario, y el proceso de reproducción utilizó memoria compartida con el proceso de la interfaz de usuario para obtener la información limitada que necesitaba para realizar la reproducción.Cuando el usuario quería detener la reproducción, el proceso de reproducción simplemente hacía un longjmp de "regreso al principio" para comenzar de nuevo, en lugar de desenredar de forma complicada dónde se estaba ejecutando cuando el usuario quería que se detuviera.Funcionó muy bien, fue simple y nunca tuve ningún problema o error relacionado con él en ese caso.

setjmp/longjmp tienen su lugar, pero ese lugar es uno que probablemente no visitarás más que de vez en cuando.

Editar:Acabo de mirar el código.En realidad fue siglongjmp() lo que usé, no longjmp (no es que sea gran cosa, pero había olvidado que siglongjmp incluso existía).

Nunca lo fue, mientras pudiste pensar por ti mismo.

Si está escribiendo una máquina virtual en C, resulta que usar gotos calculados (gcc) como este:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

Funciona mucho más rápido que el interruptor convencional dentro de un bucle.

Porque goto Puede usarse para confundir la metaprogramación.

Goto es a la vez un nivel alto y un nivel bajo controla la expresión y, como resultado, simplemente no tiene un patrón de diseño adecuado para la mayoría de los problemas.

Es nivel bajo en el sentido de que un goto es una operación primitiva que implementa algo superior como while o foreach o algo.

Es nivel alto en el sentido de que cuando se usa de ciertas maneras toma código que se ejecuta en una secuencia clara, de manera ininterrumpida, excepto en bucles estructurados, y lo transforma en piezas de lógica que son, con suficiente gotos, una caja de lógica que se reensambla dinámicamente.

Entonces, hay un prosaico y un demonio lado a goto.

El lado prosaico es que un goto que apunta hacia arriba puede implementar un bucle perfectamente razonable y un goto que apunta hacia abajo puede hacer un bucle perfectamente razonable break o return.Por supuesto, un verdadero while, break, o return sería mucho más legible, ya que el pobre humano no tendría que simular el efecto del goto para tener una visión general.Entonces, una mala idea en general.

El lado del mal implica una rutina que no usa goto por while, break o return, sino que lo usa para lo que se llama lógica de espagueti.En este caso, el desarrollador feliz con goto está construyendo piezas de código a partir de un laberinto de goto, y la única manera de entenderlo es simularlo mentalmente como un todo, una tarea terriblemente agotadora cuando hay muchos goto.Quiero decir, imagina el problema de evaluar código donde el else no es precisamente una inversa de la if, donde anidado ifPodríamos permitir la entrada de algunas cosas que fueron rechazadas por el exterior. if, etcétera etcétera.

Finalmente, para cubrir realmente el tema, debemos señalar que esencialmente todas las lenguas tempranas, excepto el algol, inicialmente hicieron sólo declaraciones únicas sujetas a sus versiones de if-then-else.Entonces, la única forma de hacer un bloqueo condicional era goto alrededor usando un condicional inverso.Una locura, lo sé, pero he leído algunas especificaciones antiguas.Recuerde que las primeras computadoras fueron programadas en código de máquina binario, así que supongo que cualquier tipo de HLL fue un salvavidas;Supongo que no fueron demasiado exigentes con respecto a las funciones HLL que obtuvieron exactamente.

Habiendo dicho todo eso yo solía pegarme uno goto en cada programa que escribí "sólo para molestar a los puristas".

Negar el uso de la declaración GOTO a los programadores es como decirle a un carpintero que no use un martillo, ya que podría dañar la pared mientras clava un clavo.Un verdadero programador sabe cómo y cuándo utilizar un GOTO.Seguí algunos de estos llamados “Programas Estructurados”. Vi un código tan horrible solo para evitar usar GOTO que podría dispararle al programador.Ok, en defensa del otro lado, también he visto código espagueti real y, nuevamente, esos programadores también deberían ser fusilados.

Aquí hay solo un pequeño ejemplo de código que encontré.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------O----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10

"En este enlace http://kerneltrap.org/node/553/2131"

Irónicamente, la eliminación del goto introdujo un error:se omitió la llamada de spinlock.

El documento original debe considerarse como "GOTO incondicional considerado perjudicial".En particular, abogaba por una forma de programación basada en condiciones condicionales (if) e iterativo (while) construcciones, en lugar de la prueba y salto común al código anterior. goto sigue siendo útil en algunos idiomas o circunstancias, donde no existe una estructura de control adecuada.

Acerca del único lugar donde estoy de acuerdo Ir a podría Se utiliza cuando es necesario lidiar con errores, y cada punto particular en el que se produce un error requiere un manejo especial.

Por ejemplo, si está tomando recursos y usando semáforos o mutex, debe tomarlos en orden y siempre debe liberarlos de la manera opuesta.

Algunos códigos requieren un patrón muy extraño para capturar estos recursos, y no se puede simplemente escribir una estructura de control fácil de mantener y entender para manejar correctamente tanto la captura como la liberación de estos recursos para evitar un punto muerto.

Siempre es posible hacerlo bien sin goto, pero en este caso y en algunos otros, Goto es en realidad la mejor solución principalmente por razones de legibilidad y mantenimiento.

-Adán

Un uso moderno de GOTO es el del compilador de C# para crear máquinas de estado para enumeradores definidos por retorno de rendimiento.

GOTO es algo que deberían utilizar los compiladores y no los programadores.

Hasta que C y C++ (entre otros culpables) hayan etiquetado pausas y continúa, goto seguirá teniendo una función.

Si GOTO en sí fuera malo, los compiladores serían malos, porque generan JMP.Si saltar a un bloque de código, especialmente siguiendo un puntero, fuera inherentemente malo, la instrucción RETurn sería mala.Más bien, el mal está en la posibilidad de abuso.

A veces he tenido que escribir aplicaciones que tenían que realizar un seguimiento de una cantidad de objetos donde cada objeto tenía que seguir una intrincada secuencia de estados en respuesta a eventos, pero definitivamente todo era un solo hilo.Una secuencia típica de estados, si se representa en pseudocódigo, sería:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

Estoy seguro de que esto no es nuevo, pero la forma en que lo manejé en C(++) fue definir algunas macros:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Luego (asumiendo que el estado es inicialmente 0), la máquina de estados estructurados anterior se convierte en el código estructurado:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

Con una variación de esto, puede haber LLAMADA y RETORNO, por lo que algunas máquinas de estados pueden actuar como subrutinas de otras máquinas de estados.

¿Es inusual?Sí.¿Se necesita algo de aprendizaje por parte del mantenedor?Sí.¿Ese aprendizaje vale la pena?Creo que sí.¿Se podría hacer sin GOTO que salten en bloques?No.

Lo evito ya que un compañero de trabajo/gerente sin duda cuestionará su uso ya sea en una revisión de código o cuando se tope con él.Si bien creo que tiene usos (el caso de manejo de errores, por ejemplo), te encontrarás con algún otro desarrollador que tendrá algún tipo de problema con él.

Que no vale la pena.

De hecho, me vi obligado a usar goto, porque literalmente no podía pensar en una manera mejor (más rápida) de escribir este código:

Tenía un objeto complejo y necesitaba realizar alguna operación en él.Si el objeto estaba en un estado, entonces podía hacer una versión rápida de la operación; de lo contrario, tenía que hacer una versión lenta de la operación.La cuestión es que en algunos casos, en medio de la operación lenta, era posible darse cuenta de que esto se podría haber hecho con la operación rápida.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

Esto estaba en una pieza de código de interfaz de usuario en tiempo real de velocidad crítica, así que honestamente creo que un GOTO estaba justificado aquí.

hugo

En casi todas las situaciones en las que se puede usar un goto, puedes hacer lo mismo usando otras construcciones.El compilador utiliza Goto de todos modos.

Personalmente, nunca lo uso explícitamente, nunca es necesario.

Una cosa que no he visto desde cualquier Una de las respuestas aquí es que una solución 'ir a' a menudo es más eficiente que una de las soluciones de programación estructurada que se mencionan a menudo.

Considere el caso de muchos bucles anidados, donde se usa 'goto' en lugar de un montón de if(breakVariable) secciones es obviamente más eficiente.La solución "Ponga sus bucles en una función y use return" suele ser totalmente irrazonable.En el caso probable de que los bucles utilicen variables locales, ahora tendrá que pasarlas todas a través de parámetros de función, lo que podría manejar muchos dolores de cabeza adicionales que surgen de eso.

Ahora considere el caso de limpieza, que yo mismo he usado con bastante frecuencia y que es tan común que presumiblemente fue responsable de la estructura try{} catch {} que no está disponible en muchos idiomas.La cantidad de comprobaciones y variables adicionales que se requieren para lograr lo mismo es mucho peor que una o dos instrucciones para dar el salto y, nuevamente, la solución de la función adicional no es una solución en absoluto.No puedes decirme que es más manejable o más legible.

Ahora bien, el espacio de código, el uso de la pila y el tiempo de ejecución pueden no ser lo suficientemente importantes en muchas situaciones para muchos programadores, pero cuando estás en un entorno integrado con solo 2 KB de espacio de código para trabajar, 50 bytes de instrucciones adicionales para evitar una claramente definida. 'goto' es simplemente ridículo, y esta no es una situación tan rara como creen muchos programadores de alto nivel.

La afirmación de que "goto es perjudicial" fue muy útil para avanzar hacia la programación estructurada, incluso si siempre fue una generalización excesiva.A estas alturas, todos lo hemos escuchado lo suficiente como para desconfiar de usarlo (como deberíamos).Cuando obviamente es la herramienta adecuada para el trabajo, no debemos tenerle miedo.

Puede usarlo para salir de un bucle profundamente anidado, pero la mayoría de las veces su código se puede refactorizar para que sea más limpio sin bucles profundamente anidados.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top