GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué algunos programadores del kernel usan goto en lugar de bucles while simples?

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


Linux
  1. Por qué a los programadores les encanta el empaquetado de Linux

  2. ¿Por qué Ubuntu 14.04 Lts usa una versión del kernel que no es lts?

  3. Error de "mapa en uso" al eliminar el dispositivo de rutas múltiples en CentOS/RHEL

  4. ¿Por qué se debe evitar eval en Bash y qué debo usar en su lugar?

  5. ¿Por qué se usa u8 u16 u32 u64 en lugar de int sin firmar en la programación del kernel?

Por qué uso exa en lugar de ls en Linux

Por qué uso rxvt como mi terminal

Cómo usar bucles en Ansible Playbook

Las 10 razones principales por las que usar Linux

Por qué los nerds usan Linux

Linux vs Mac OS:15 razones por las que usar Linux en lugar de Mac OS