La instrucción GOTO. ¿Debo usarla?

Por J. F. Díaz (jfdiaz98@hotmail.com)
Lic. en Ciencias de la Computación

El GOTO1 es una instrucción de salto incondicional que permite transferir el control de ejecución directamente a otro punto de un mismo programa, el cual debe estar rotulado con un identificador especial llamado etiqueta. Constituye un mecanismo vital para programar en lenguajes primitivos como COBOL, BASIC y Ensamblador por cuanto permite simular construcciones de control estructuradas de las cuales carecen dichos lenguajes, tales como los ciclos y estructuras de selección múltiple (CASE/switch). El Ensamblador incluso proporciona un amplio conjunto de instrucciones de salto --variantes del GOTO-- sin las cuales sería prácticamente imposible producir programas de importancia.

La instrucción GOTO no es patrimonio exclusivo de los lenguajes de programación primitivos. Esta instrucción se encuentra disponible también en una gran cantidad de lenguajes modernos principalmente por razones de compatibilidad con sus versiones previas o las de otros fabricantes, y como una herramienta alternativa para ayudar a la resolución de problemas. De hecho, muchos lenguajes modernos deben a esta instrucción gran parte de su popularidad, puesto que permite a los programadores veteranos y a los que usan versiones anteriores de dichos lenguajes (u otros lenguajes primitivos) continuar con el viejo estilo de programación al que están acostumbrados. Sin embargo, muchos programadores de la actualidad abusan de las instrucciones de salto por lo fácil que resulta programar así, aunque el código resultante se vuelva un laberinto difícil de comprender, probar, depurar y mantener por la gran cantidad de saltos hacia atrás y hacia delante que contiene.

El uso del GOTO (y de las intrucciones de salto en general) dio origen a acalorados debates en los círculos de programadores. Tales debates descansaron en las diferencias de opiniones entre los programadores que usaban el GOTO como arma fundamental en la solución de problemas de programación (aunque dispusieran de lenguajes o mecanismos que permitieran implementar diseños más estructurados), y los que preferían emplear las técnicas de diseño estructurado y las instrucciones de control que ofrecen los lenguajes modernos, evitando así el estilo pobre de programación y las desventajas que representa su uso.

En realidad, la instrucción GOTO tiene tanto ventajas que aprovechar como desventajas que evitar y su uso tiene que ver más con el estilo de programación mismo que con los lenguajes que implementan la instrucción. Exponer esas ventajas y estas desventajas y ofrecer razones para el uso apropiado del GOTO es el objetivo principal de este artículo.

Ventajas y argumentos a favor del GOTO
El argumento más importante en favor del GOTO se fundamenta más por su uso apropiado en circunstancias específicas y no en su uso indiscriminado, ya que existen determinadas situaciones donde el uso del GOTO es la única forma de resolver un problema de programación.

Otro argumento válido es que un GOTO bien ubicado puede eliminar código duplicado. El código duplicado conduce a problemas si se olvida modificar una de las copias durante las fases de depuración y mantenimiento, dejando el código inconsistente y sembrando errores muy difíciles de detectar, además de incrementar el tamaño de los programas fuente y los archivos ejecutables. En tales casos, las desventajas del uso de una instrucción GOTO (mencionadas más adelante) se ven mínimas frente al efecto de código duplicado.

La posibilidad que ofrece el GOTO de permitir simular construcciones de control estructuradas ausentes en lenguajes de programación primitivos es uno de los argumentos fuertes a su favor. El GOTO acoplado con sentencias ifs, permite al usuario la definición de mecanismos de flujo de control necesarios de la misma forma que los tipos de datos definidos por el usuario le permiten la definición de representaciones de datos que no se disponen en el lenguaje. Tanto las proposiciones GOTO como los tipos de datos definidos por el usuario pueden usarse para ampliar los objetivos de sencillez, claridad y elegancia de los programas.

La facilidad que permite el GOTO de alterar la linealidad del flujo de un programa es otro argumento a su favor. Esta última facilidad permite diseñar mecanismos prácticos para crear rutas alternas de entrada o salida a/de un bloque de código y lograr un resultado deseado constituye otro de los argumentos fuertes a favor del GOTO. Un caso clásico de esto último son los ciclos que deben tener varias condiciones de terminación. Si se intentara reemplazar este código con uno que no haga uso de GOTO el código resultante sería más largo, quizá menos eficiente y de seguro más complejo que el original, lo que dificultaría mucho su comprensión.

Otra de las razones para el uso de esta instrucción es su utilidad en las rutinas que asignan recursos del sistema y en las rutinas de limpieza durante la recuperación de errores del programa. En ambos casos puede usarse el GOTO para saltar a un bloque de código especial que se encargue de devolver los recursos al sistema si solo se consigue una parte de los que se requieren para una operación, y cuando se requiera concluir apropiadamente un programa fallido, manejando adecuadamente los errores y reduciendo así los riesgos de afectar otros programas, otros usuarios y a los datos mismos.

Como últimas ventajas del uso del GOTO mencionaremos la rapidez de desarrollo de los programas, y en algunos casos la mayor velocidad y el menor tamaño del código resultante en contraste con código más elaborado que emplea estructuras de control.

Desventajas y argumentos en contra del GOTO
Una de las desventajas del uso del GOTO es que conduce a código de más baja calidad por cuanto es más difícil comprender, depurar, probar y mantener programas que emplean esta instrucción. Además, este tipo de código es más difícil de formatear mediante la indentación. Aunque esta técnica de formateo no es relevante para los compiladores al momento de compilar o ejecutar los programas, es utilizada para mostrar la estructura lógica del código y hacerlo más legible. Puesto que las instrucciones GOTO tienen un efecto directo sobre la estructura lógica de los programas, es muy difícil mostrar dicha estructura en un programa que emplee GOTO mediante la indentación, y permitir así una mejor comprensión del código por parte de los humanos.

Otra de las desventajas del GOTO es que obstaculiza las optimizaciones del compilador. Puesto que algunas optimizaciones dependen de que el control de flujo de un programa permanezca dentro de un área específica de código, las instrucciones GOTO limitan y dificultan la habilidad de los compiladores para analizar el flujo de control y hacer sus optimizaciones cuando saltan a regiones lejanas. Aunque se obtengan ganancias de eficiencia en el proceso de la programación mediante el empleo de instrucciones GOTO en el código fuente, se puede reducir la eficiencia general al frustrarse las optimizaciones que puede hacer el compilador.

En el caso del uso del GOTO para saltar a rutinas de limpieza y liberación de recursos, el empleo de subprogramas en los lenguajes modernos evita la necesidad de su utilización. Cada instrucción GOTO puede reemplazarse con una llamada a un subprograma que se encargue de este trabajo.

Aunque el GOTO permite efectivamente simular estructuras de control en lenguajes primitivos, en las situaciones en la que se utilicen lenguajes de programación modernos se recomienda emplear las que éstos ofrecen para evitar la creación de código con un estilo de programación pobre, defectuoso y de baja calidad. Los compiladores modernos están programados para optimizar mejor este tipo de instrucciones, por lo que evitarlas usando GOTO se pierden las ventajas en eficiencia que pudiera aportar el compilador.

Por otro lado no siempre se obtiene rapidez de desarrollo, mayor velocidad y menor tamaño del código resultante al utilizar GOTO. Aunque existen situaciones particulares en la que esto es cierto, por lo general depende mucho del programador, del problema a resolver y del compilador. Una gran cantidad de código puede ser reescrito de manera inteligente para mejorar su desempeño y aprovechar las capacidades de optimización de los compiladores, y hasta resultan más naturales y comprensibles si se emplean las instrucciones de control que ofrecen los lenguajes modernos en lugar de instrucciones GOTO.

Aunque es muy común tener varias condiciones para terminar un ciclo, el mejor procedimiento para lidiar con estas situaciones es rediseñar el algoritmo de modo que no se requieran las salidas múltiples. Si esto no es posible (uno no debe llegar a esta conclusión demasiado pronto), una técnica para lograr las salidas múltiples de los ciclos es introducir una variable booleana bandera para cada condición y probar las banderas en cada iteración del ciclo. Aunque se necesiten variables y pruebas adicionales a la salida del ciclo para determinar cuál condición causó la salida, dada las capacidades de almacenamiento y las velocidades de los ordenadores de hoy es posible sacrificar eficiencia por legibilidad.

La gran cantidad de casos en que se requiera que el ciclo termine inmediatamente después de la ocurrencia de una condición dada y no al inicio de la siguiente iteración, como ocurriría al probar una bandera del ciclo, pueden paliarse con el uso de la instrucción EXIT o BREAK que ofrecen muchos lenguajes modernos para abandonar los ciclos rápidamente. En las situaciones donde esto no sea posible, y una solución razonable es usar instrucciones GOTO para lograr las salidas múltiples del ciclo y aislar así las acciones alternativas en la salida, manteniendo el espíritu de claridad y localidad al transferir el control sólo a la vecindad de la salida del ciclo. Sin embargo uno no debe apresurarse a crear las salidas múltiples. Con frecuencia los algoritmos pueden rediseñarse para eliminar la necesidad de las salidas múltiples.

Además de todo lo anterior, cabe mencionar los estudios y artículos que Steve McConnell incluye en la de su excelente libro Code Complete, los cuales apoyan los argumentos en contra del uso del GOTO. Al final de dicha sección McConnell ofrece además una lista de documentos clásicos de programación de prestigiosos autores que contienen argumentos a favor y en contra del uso del GOTO.

Si bien la instrucción GOTO puede usarse en la programación de una manera positiva, debe reservarse para situaciones raras o poco comunes donde tiene que romperse la estructura natural de un algoritmo. Una buena regla es evitar el uso de saltos con GOTO para expresar la iteración regular o la ejecución condicional de proposiciones, pues los saltos tienden a violar los principios de la programación estructurada. Además, la claridad del código se ve extremadamente perjudicada y se hacen mucho más difíciles las tareas de verificación y mantenimiento del código.

Aunque a menudo la instrucción GOTO puede usarse con efecto positivo sobre la claridad de un programa, es una instrucción de muy bajo nivel (análoga a una operación de salto a nivel de máquina), y con facilidad se abusa de ella. Un problema serio al usar GOTO es la insidiosa postura mental que resulta de pensar en términos de esta instrucción. Por ejemplo, el código para una selección de cuatro alternativas puede instrumentarse utilizando sentencias if junto con proposiciones GOTO para dirigir el flujo a una etiqueta específica. En los lenguajes de programación primitivos, esta puede ser la mejor estructura que se puede lograr. Una estructura preferible es la anidación de sentencias if que es más compacta y es soportada por los lenguajes de programación modernos.

Sin embargo, si se está acostumbrado a programar con GOTO, la solución más clara y compacta que resulta de las construcciones if anidadas puede no venir a la mente tan fácil como la versión con GOTO, aún cuando la instrucción if esté disponible en el lenguaje de programación que se utiliza.

En resumen, la proposición GOTO puede ser un mecanismo valioso cuando se usa de una manera disciplinada y con estilo. Como regla general los GOTO hacia delante para salir de construcciones locales y transferir el control hacia el código local (cercano) para manejo de errores son aceptables. Las instrucciones GOTO que transfieren el control a regiones remotas del código, o que saltan adentro y afuera de segmentos de código en formas complicadas, destruyen la linealidad y localidad del flujo de control. Deben evitarse.

Cuándo usar GOTO
La gente a favor del GOTO usualmente presentan ejemplos triviales de código en los cuales la eliminación de la instrucción representa código más largo, duplicado, con comparaciones y variables extras que lo hacen complejo. Los que están en contra del GOTO presentan también ejemplos triviales de código que son fáciles de rescribir sin GOTO. Las versiones resultantes son quizá más largas y menos rápidas, pero son más fáciles de comprender y mantener. Este último enfoque es preferible dado que el hardware actual permite, en aras de la legibilidad y la facilidad de modificación, sacrificar velocidad y espacio de almacenamiento.

La literatura tampoco ayuda mucho. Gran cantidad de libros –con honrosas excepciones-- ofrecen también ejemplos triviales de reescritura de código sin GOTO. Sin embargo, los casos triviales no constituyen un gran porcentaje de los casos más comunes de programación y tal aporte es meramente ilustrativo de cómo deberían recodificarse sin usar GOTO. Una honrosa excepción es el libro antes mencionado de Steve McConnell, Code Complete, en cuya sección 16.1 presenta varios casos de reescritura de código sin GOTO en Pascal referente al procesamiento de archivos dentro de ciclos, manejo de errores y devolución de recursos sin anidamiento profundo de if usando variables de estado, y reestructuración de código mediante comprobaciones inteligentes de variables y eliminación de código compartido reemplazándolo con llamadas a subprogramas.

El uso de GOTO es para mí una cuestión de escuela y de costumbre. Los lenguajes modernos implementan esta instrucción no para fomentar un estilo perezoso de programación ni la violación de las prácticas de codificación estructuradas, mucho menos para instar a la creación de código de baja calidad. La implementan como una herramienta más para ayudar a resolver ese pequeño porcentaje de casos en los que su utilización sea la única forma de conseguir un objetivo. Fácilmente puede reemplazarse casi el 100% de las instrucciones GOTO con código equivalente usando construcciones estructuradas. La difícil fracción restante, después de intentar eliminarla rompiendo el código en rutinas más pequeñas, usar if anidados, probar y reprobar variables de estado o reestructurar condicionales, será la que merezca realmente ser implementada mediante saltos y serán muy pocos los que se opongan a ello.

Los casos donde el uso del GOTO sean para ti soluciones legítimas a problemas deben documentarse claramente y usarlas sin remordimientos de conciencia. No obstante, mantente abierto a las sugerencias de otros programadores, quizá más experimentados, que pueden ver algo que tú no puedes.

Sugerencias de uso del GOTO
Para finalizar se presenta una lista de sugerencias para el uso disciplinado del GOTO:
  • Usa instrucciones GOTO para simular construcciones de control estructuradas en lenguajes que no las soporten directamente. Pero asegúrate de simularlas exactamente. No abuses de la extra flexibilidad que te da el GOTO.

  • No uses instrucciones GOTO cuando una construcción estructurada esté disponible.

  • Mide el desempeño de cualquier GOTO usado para mejorar la eficiencia. En la mayoría de los casos que usan GOTO, puedes recodificarlos sin instrucciones GOTO con legibilidad mejorada y sin pérdida de eficiencia. Si tu caso es la excepción, documenta la mejora en eficiencia que da el GOTO, de tal manera que no sea removida por los que se oponen a esta instrucción cuando la vean.

  • Limítate tú mismo a una etiqueta GOTO por rutina, a menos que estés simulando construcciones estructuradas.

  • Limítate tú mismo a instrucciones GOTO que salten hacia delante, no hacia atrás, a menos que estés simulando construcciones estructuradas.

  • Asegúrate que todas las etiquetas GOTO sean usadas. Ellas podrían ser un síntoma de código faltante (el que salta hacia ellas) o la muestra de un estilo pobre de programación. Si no usas esa etiquetas, elimínalas.

  • Asegúrate que las instrucciones GOTO no creen código inalcanzable. Para cada GOTO crea su etiqueta y bloque de código respectivo.

  • Si eres un jefe de programadores, no seas inflexible cuando uno de ellos use algún GOTO. Escucha en cambio sus razones. Si el programador está advertido de las alternativas y está dispuesto a argumentar su uso, probablemente el GOTO esté siendo bien empleado.


1 No debe confundirse la instrucción GOTO con su homónima (o su variante GO) empleada en los lenguajes de manejo de bases de datos para el desplazamiento del puntero de una tabla de un registro a otro.


Ir al inicio de este artículo | Versión imprimible
Ir a la Página Principal de NeoProgramadores

Lecturas Relacionadas

Métodos de Prueba de Programas

Las Fases de la Programación

La Importancia de la Prueba del Software

Cómo Ser un Buen Codificador de Software


¡Mándame un mensaje!
alojamiento web gratis
Otros servicios ofrecidos por HispaVista:
Inmobiliaria y Dominios
Consigue una página web gratis o un
alojamiento web profesional con Galeón