Como outros já disseram, o problema não está no goto
próprio; o problema está em como as pessoas usam goto
e em como isso pode dificultar o entendimento e a manutenção do código.
Suponha o seguinte trecho de código:
i = 4;
label: printf( "%d\n", i );
Para que valor é impresso i
? Quando é impresso? Até que você responda a todas as instâncias da goto label
sua função, você não poderá saber. A simples presença desse rótulo destrói sua capacidade de depurar código por meio de uma simples inspeção. Para pequenas funções com uma ou duas ramificações, não há muito problema. Para funções não pequenas ...
No início dos anos 90, recebemos uma pilha de código C que dirigia uma tela gráfica em 3D e nos mandava fazê-la funcionar mais rapidamente. Eram apenas cerca de 5000 linhas de código, mas todas estavam dentro main
, e o autor usou cerca de 15 ou mais goto
ramificações nas duas direções. Este era um código ruim , para começar, mas a presença deles goto
tornava tudo muito pior. O meu colega de trabalho levou cerca de duas semanas para descobrir o fluxo de controle. Melhor ainda, esses goto
resultados resultaram em um código tão fortemente associado a si mesmo que não pudemos fazer nenhuma alteração sem quebrar alguma coisa.
Tentamos compilar com a otimização de nível 1, e o compilador consumiu toda a RAM disponível, depois toda a troca disponível e depois entrou em pânico no sistema (que provavelmente não tinha nada a ver com o goto
próprio s, mas eu gosto de contar essa história).
No final, demos ao cliente duas opções - vamos reescrever tudo do zero ou comprar hardware mais rápido.
Eles compraram hardware mais rápido.
Regras do Bode para usar goto
:
- Ramificar apenas para frente;
- Não ignorar as estruturas de controle (ou seja, não se ramificar para o corpo de um
if
ou for
ou while
declaração);
- Não use
goto
no lugar de uma estrutura de controle
Há casos em que a goto
é a resposta certa, mas eles são raros (romper um loop profundamente aninhado é o único lugar em que eu o uso).
EDITAR
Expandindo essa última declaração, aqui está um dos poucos casos de uso válidos para goto
. Suponha que temos a seguinte função:
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;
}
Agora, temos um problema - e se uma das malloc
chamadas falhar no meio? Por mais improvável que seja um evento, não queremos retornar uma matriz parcialmente alocada, nem queremos apenas sair da função com um erro; queremos limpar a nós mesmos e desalocar qualquer memória parcialmente alocada. Em um idioma que lança uma exceção em uma alocação incorreta, isso é bastante simples - basta escrever um manipulador de exceções para liberar o que já foi alocado.
Em C, você não possui manipulação de exceção estruturada; você deve verificar o valor de retorno de cada malloc
chamada e executar a ação apropriada.
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 fazer isso sem usar goto
? É claro que podemos - isso requer apenas um pouco de contabilidade extra (e, na prática, esse é o caminho que eu seguiria). Mas, se você está procurando lugares onde o uso de um goto
não é imediatamente um sinal de má prática ou design, esse é um dos poucos.