Como otros han dicho, el problema no está en gotosí mismo; El problema es cómo usan las personas gotoy cómo puede dificultar la comprensión y el mantenimiento del código.
Suponga el siguiente fragmento de código:
i = 4;
label: printf( "%d\n", i );
¿Para qué valor se imprime i? ¿Cuándo se imprime? Hasta que no tenga en cuenta cada instancia de goto labelsu función, no puede saberlo. La simple presencia de esa etiqueta destruye su capacidad de depurar código mediante una simple inspección. Para funciones pequeñas con una o dos ramas, no es un gran problema. Para funciones no pequeñas ...
A principios de los años 90, nos dieron una pila de código C que manejaba una pantalla gráfica en 3D y nos dijo que hiciera que funcionara más rápido. Solo tenía alrededor de 5000 líneas de código, pero todo estaba dentro main, y el autor usó aproximadamente 15 gotoramificaciones en ambas direcciones. Para empezar, este era un código malo , pero la presencia de esos gotos lo hizo mucho peor. A mi compañero de trabajo le tomó alrededor de 2 semanas descifrar el flujo de control. Aún mejor, esos gotos resultaron en un código tan fuertemente acoplado consigo mismo que no pudimos hacer ningún cambio sin romper algo.
Intentamos compilar con la optimización de nivel 1, y el compilador consumió toda la RAM disponible, luego todo el intercambio disponible, y luego entró en pánico en el sistema (que probablemente no tuvo nada que ver con los gotos, pero me gusta lanzar esa anécdota).
Al final, le dimos al cliente dos opciones: reescribir todo desde cero o comprar hardware más rápido.
Compraron hardware más rápido.
Reglas de Bode para usar goto:
- Ramificarse solo hacia adelante;
- No pasar por alto las estructuras de control (es decir, no se ramifican en el cuerpo de un
ifo foro whiledeclaración);
- No utilizar
gotoen lugar de una estructura de control.
Hay casos en los que a goto es la respuesta correcta, pero son raros (salir de un bucle profundamente anidado es el único lugar donde lo usaría).
EDITAR
Ampliando esa última declaración, este es uno de los pocos casos de uso válidos para goto. Supongamos que tenemos la siguiente función:
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
for ( i = 0; i < N; i ++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
for ( j = 0; j < M; j++ )
{
arr[i][j] = malloc( sizeof *arr[i][j] * P );
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
return arr;
}
Ahora, tenemos un problema: ¿qué sucede si una de las mallocllamadas falla a la mitad? No es probable que sea un evento como ese, no queremos devolver una matriz parcialmente asignada, ni queremos simplemente abandonar la función con un error; queremos limpiar después de nosotros mismos y desasignar cualquier memoria parcialmente asignada. En un lenguaje que arroja una excepción en una mala asignación, eso es bastante sencillo: simplemente escribe un controlador de excepciones para liberar lo que ya se ha asignado.
En C, no tiene un manejo estructurado de excepciones; debe verificar el valor de retorno de cada mallocllamada y tomar las medidas apropiadas.
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( i = 0; i < N; i ++ )
{
if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
goto cleanup_1;
for ( j = 0; j < M; j++ )
{
if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
goto cleanup_2;
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
}
goto done;
cleanup_2:
// We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
while ( j-- )
free( arr[i][j] );
free( arr[i] );
// fall through
cleanup_1:
// We failed while allocating arr[i]; free up all previously allocated arr[i][j]
while ( i-- )
{
for ( j = 0; j < M; j++ )
free( arr[i][j] );
free( arr[i] );
}
free( arr );
arr = NULL;
done:
return arr;
}
¿Podemos hacer esto sin usar goto? Por supuesto que podemos, solo requiere un poco de contabilidad adicional (y, en la práctica, ese es el camino que tomaría). Pero, si está buscando lugares donde usar un gotono sea inmediatamente una señal de mala práctica o diseño, este es uno de los pocos.