sentencia - ¿Por qué algunos programadores kernel usan goto en lugar de bucles while simples?
sentencia break en c (2)
Cuando aprendí C, la maestra me dijo todo el día: "No uses goto, es un mal hábito, es feo, ¡es peligroso!" y así.
¿Por qué entonces, algunos programadores de kernel usan goto
, por ejemplo en esta función , donde podría ser reemplazado por un simple
while(condition) {}
o
do {} while(condition);
No puedo entender eso. ¿Es mejor en algunos casos usar goto en lugar de while
/ do
- while
? Y si es así, ¿por qué?
En el caso de este ejemplo, sospecho que se trataba de adaptar el soporte de SMP al código que se escribió originalmente de una manera que no es SMP. Añadiendo un goto again;
la ruta es mucho más simple y menos invasiva que la reestructuración de la función.
No puedo decir que me gusta mucho este estilo, pero también creo que es una equivocación evitarlo por razones ideológicas. Un caso especial de uso de goto
(diferente de este ejemplo) es donde goto
solo se usa para avanzar en una función, nunca al revés. Esta clase de usos nunca da como resultado construcciones de bucle que surgen de goto
, y es casi siempre la forma más simple y clara de implementar el comportamiento necesario (que generalmente es limpiar y volver al error).
Contexto histórico: debemos recordar que Dijkstra escribió Goto Considered Harmful 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.
Analisis de CASO:
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 ciclo". Cuando miro la versión goto
, pienso en ella como una línea recta con un caso "try again" al final.
La versión goto
tiene SETUP
y COMPUTE SOME VALUES
en la misma columna, lo que enfatiza que la mayor parte del tiempo, el flujo de control pasa por ambos. La versión estructurada coloca SETUP
y COMPUTE SOME VALUES
en diferentes columnas, lo que enfatiza que el control puede pasar a través de ellos de manera diferente.
La pregunta aquí es ¿qué tipo de énfasis quieres poner en el código? Puede 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) {
...
Versión de Goto:
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 una preocupación tipográfica, como la sangría.