就像其他人所说的那样,问题不在goto
于其本身,而是它本身。问题在于人们的使用方式goto
,以及如何使代码难以理解和维护。
假定以下代码段:
i = 4;
label: printf( "%d\n", i );
打印什么值i
? 什么时候打印?在不考虑函数中的每个实例之前goto label
,您都不知道。该标签的简单存在会破坏您通过简单检查来调试代码的能力。对于具有一个或两个分支的小型功能,问题不大。对于不小的功能...
早在90年代初,我们就得到了一大堆C代码,它们驱动3D图形显示并被告知使其运行更快。它只有大约5000行代码,但所有代码都在中main
,并且作者goto
在两个方向上使用了大约15个左右的分支。开头的代码很糟糕,但是这些代码的存在goto
使它变得更糟。我的同事花了大约2周的时间才能弄清控制流程。更好的是,这些goto
导致代码与自身紧密耦合,以至于我们在不破坏某些内容的情况下无法进行任何更改。
我们尝试使用1级优化进行编译,然后编译器先吞噬了所有可用的RAM,然后吞噬了所有可用的交换空间,然后恐慌了系统(这可能与goto
s本身无关,但是我喜欢把那个轶事扔在那里)。
最后,我们为客户提供了两种选择-让我们从头开始重写整个内容,或者购买速度更快的硬件。
他们购买了更快的硬件。
博德的使用规则goto
:
- 仅向前分支;
- 不要绕过控制结构(即不要分支到
if
or for
或while
statement 的主体中);
- 不要
goto
代替控制结构使用
在某些情况下,a goto
是正确的答案,但很少见(突破深层嵌套的循环是我唯一使用它的地方)。
编辑
扩展最后一条语句,这是的几个有效用例之一goto
。假设我们具有以下功能:
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;
}
现在,我们有一个问题-如果其中一个malloc
呼叫中途失败怎么办?不太可能发生这样的事件,我们既不希望返回部分分配的数组,也不想因错误而退出函数。我们想自己清理一下并释放所有部分分配的内存。用一种在错误的分配上引发异常的语言,这非常简单-您只需编写一个异常处理程序以释放已分配的内容。
在C语言中,您没有结构化的异常处理。您必须检查每个malloc
调用的返回值并采取适当的措施。
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;
}
我们可以不用使用它goto
吗?我们当然可以-它只需要做一些额外的簿记(实际上,这就是我要走的路)。但是,如果您正在寻找使用a goto
并非立即表明不良做法或设计的迹象的地方,这就是少数几个。