Contexto histórico: Debemos recordar que Dijkstra escribió Pasar a Considerado Dañino en 1968, cuando muchos programadores usaban goto
como reemplazo de la programación estructurada (if
, while
, for
, etc.).
Han pasado 44 años y es raro encontrar este uso de goto
En la naturaleza. La programación estructurada ya ganó, hace mucho tiempo.
Análisis de casos:
El código de ejemplo se ve así:
SETUP...
again:
COMPUTE SOME VALUES...
if (cmpxchg64(ptr, old_val, val) != old_val)
goto again;
La versión estructurada se ve así:
SETUP...
do {
COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);
Cuando miro la versión estructurada, inmediatamente pienso, "es un bucle". Cuando miro el goto
versión, creo que es una línea recta con un caso de "intentar de nuevo" al final.
El goto
la versión tiene ambos SETUP
y COMPUTE SOME VALUES
en la misma columna, lo que enfatiza que la mayor parte del tiempo, el flujo de control pasa a través de ambos. La versión estructurada pone SETUP
y COMPUTE SOME VALUES
en diferentes columnas, lo que enfatiza que el control puede pasar a través de ellas de manera diferente.
La pregunta aquí es qué tipo de énfasis desea poner en el código. Puedes comparar esto con goto
para el manejo de errores:
Versión estructurada:
if (do_something() != ERR) {
if (do_something2() != ERR) {
if (do_something3() != ERR) {
if (do_something4() != ERR) {
...
Ir a versión:
if (do_something() == ERR) // Straight line
goto error; // |
if (do_something2() == ERR) // |
goto error; // |
if (do_something3() == ERR) // |
goto error; // V
if (do_something4() == ERR) // emphasizes normal control flow
goto error;
El código generado es básicamente el mismo, por lo que podemos considerarlo como un problema tipográfico, como una sangría.
Muy buena pregunta, y creo que solo los autores pueden dar una respuesta definitiva. Agregaré mi parte de especulación al decir que podría haber comenzado usándolo para el manejo de errores, como lo explica @Izkata y que las puertas también estaban abiertas para usarlo para bucles básicos.
En mi opinión, el uso del manejo de errores es legítimo en la programación de sistemas. Una función asigna memoria gradualmente a medida que avanza, y si se encuentra un error, goto
la etiqueta adecuada para liberar los recursos en orden inverso desde ese punto.
Entonces, si el error ocurre después de la primera asignación, saltará a la última etiqueta de error, para liberar solo un recurso. Asimismo, si el error se produce después de la última asignación, saltará a la primera etiqueta de error y se ejecutará desde allí, liberando todos los recursos hasta el final de la función. Tal patrón para el manejo de errores aún debe usarse con cuidado, especialmente cuando se recomienda modificar el código, valgrind y las pruebas unitarias. Pero podría decirse que es más legible y fácil de mantener que los enfoques alternativos.
Una regla de oro para usar goto
es evitar el llamado código espagueti. Intenta dibujar líneas entre cada goto
declaración y su respectivo rótulo. Si tienes líneas que se cruzan, bueno, has cruzado una línea :). Tal uso de goto
es muy difícil de leer y una fuente común de errores difíciles de rastrear, ya que se encontrarían en lenguajes como BASIC que dependían de él para el control de flujo.
Si solo hace un bucle simple, no obtendrá líneas cruzadas, por lo que aún es legible y mantenible, convirtiéndose en gran medida en una cuestión de estilo. Dicho esto, dado que se pueden hacer con la misma facilidad con las palabras clave de bucle proporcionadas por el idioma, como indicó en su pregunta, mi recomendación aún sería evitar usar goto
para bucles, simplemente porque el for
, do/while
o while
las construcciones son más elegantes por diseño.
En el caso de este ejemplo, sospecho que se trataba de actualizar el soporte de SMP en un código que originalmente se escribió de una manera no segura para SMP. Añadir un goto again;
path es mucho más simple y menos invasivo que reestructurar la función.
No puedo decir que me guste mucho este estilo, pero también creo que es un error evitar goto
por razones ideológicas. Un caso especial de goto
el uso (diferente de este ejemplo) es donde goto
solo se usa para avanzar en una función, nunca hacia atrás. Esta clase de usos nunca da como resultado construcciones de bucle que surgen de goto
, y casi siempre es la forma más simple y clara de implementar el comportamiento necesario (que generalmente es limpiar y regresar en caso de error).