为goto正名

平反goto语句
部署运行你感兴趣的模型镜像

昨天和老板一块儿review我的code,当看到我的代码中存在一些goto语句的时候,老板条件反射般严肃的问我:“为什么要用goto语句”?

我很理解她为什么会惊讶。自从计算机科学一代宗师Dijkstra于1968年发表了著名的文章《Go To Statement Considered Harmful》之后,goto语句就成了过街老鼠,人人喊打。甚至有人开玩笑说:“今天你敢用goto,明天老板就让你go home”。

“不要在程序中使用goto”已经成为绝大多数开发者的圣经,但却很少有人认真的思考过,为什么会这样?

goto问题之所以这么臭名昭著,从历史的角度来看,在Dijkstra在ACM发表那篇文章之前,现代软件构造方法还没有出现,goto语句是当时实现流程控制的主要手段。那篇文章发表之后,引发了大量的争论,并最终导致了当代程序员认为理所当然的结构化编程的出现与风靡。

但有趣的地方也正在与此,结构化编程是由goto问题的争论产生的。从时间的顺序上讲,结构化编程语言Pascal,C及现代面向对象语言C++,Java,C#均出现在1968年之后,但为什么这些语言都保留了goto或与goto功能相近的语句?

所以,问题本身并不在于goto。Dijkstra之所以竭力反对goto的原因是因为泛滥的使用goto将会导致软件难以理解和跟踪。所以,我们要反对的是“难以理解和跟踪”的程序,而不是goto语句。

goto语句一定会造成程序“难以理解和跟踪”吗?不!恰恰相反,在适当的地方适当的使用goto恰恰可以让程序更加容易的理解和跟踪,甚至让代码更有效率。举个例子:

while(1) {
   ...
   do {
      ...
      for(i=0; i < max; i++) {
         if(a[i] == 10)
            goto done; 
         else
            count += a[i];
      }
      ...
   } while( n > 0);
   ...
}

done:
...

由于break只能跳出一级循环,在很深的循环嵌套中,如果存在一个从结束所有循环的需求,则goto是最能直接体现此意图,并让程序更简捷的手段。否则上面的代码不得不写为:

boolean finished = false;
while(1) {
   ...
   do {
      ...
      for(int i=0; i < max; i++) {
         if(a[i] == 10) {
            finished = true;
            break;
         }
         else
            count += a[i];
      }
      if(finished)
         break;
      ... 
   } while( n > 0);
  
   if(finished)
      break;
   ...
}

再看另外一个例子:

#define FREE(p) if(p) { /
     free(p); /
     p = NULL; /
   }
void foo()
{
   char *fname=NULL, *lname=NULL, *mname=NULL;
   fname = ( char* ) calloc ( 20, sizeof(char) );
   if ( fname == NULL ){
      goto ErrHandle;
   }
  
   lname = ( char* ) calloc ( 20, sizeof(char) );
   if ( lname == NULL ){
      goto ErrHandle;
   }
  
   mname = ( char* ) calloc ( 20, sizeof(char) );
   if ( mname == NULL ){
      goto ErrHandle;
   }

   ......

ErrHandle:
   FREE(fname);
   FREE(lname);
   FREE(mname);
   ReportError(ERR_NO_MEMOEY);
}

在这种情况下,goto语句会让你的程序更易读,更容易维护。

还有其它良好使用goto的例子,这里就不一一列举了。

孔子曰:“过犹不及”。教条主义般的反对goto,其原因和结果都只能是本末倒置。我们的核心思想是:构造易于阅读和理解的代码。只要抓住这一点,我们就能以平常心看待goto。不使用goto,仍然可以写出非常糟糕的代码;而使用了goto语句的程序,却很有可能是优秀的代码。事实上,goto和其它语句及机制一样,都只不过是为我们这个核心思想服务的手段或工具罢了。

自从1968年,goto语句就背负了它不该承受的恶名。到了为其平反的时候了。

 

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<think>好的,用户问为什么不推荐使用goto。首先我需要回忆C语言goto的基本用法和常见问题。goto允许程序跳转到同一函数内的标签处,这在某些情况下可能有用,比如错误处理或者跳出多层循环。 然后,我应该考虑为什么goto不被推荐。主要原因是结构化编程的提倡,goto会破坏代码的结构,导致流程难以跟踪。比如,过多使用goto会让代码像面条一样混乱,增加维护难度。 接下来需要列举具体的缺点。比如可读性下降,逻辑变得复杂,容易引入错误。特别是当goto向前跳转时,可能会跳过变量初始化,导致未定义行为。此外,使用goto可能导致资源泄漏,比如忘记释放内存或关闭文件。 不过,也要提到goto的合理使用场景,比如集中处理错误情况,这样可以在函数退出前统一释放资源。这种情况下,goto可能比多层嵌套的if语句更清晰。 然后需要给出替代方案,比如使用循环控制语句break、continue,或者将代码拆分为函数,使用return提前退出。结构化的控制流语句更容易理解和维护。 最后要给出一个例子,比较使用goto和替代方法的代码。比如错误处理时,用goto集中清理资源,或者用条件判断和循环结构替代goto的使用。 总结时,要强调虽然goto在特定情况下有用,但大多数情况下应优先使用结构化的方法,以提高代码的可读性和可维护性。 现在整理这些思路,分点解释为什么不推荐使用goto,并给出示例代码和替代方案。然后按照要求提出相关问题,确保问题与goto、控制流、代码结构等相关。</think><< 不推荐使用`goto`的主要原因在于它会破坏代码的结构化,导致程序流程难以追踪和维护。以下是具体原因及替代方案: ### 不推荐使用`goto`的原因: 1. **破坏结构化编程** `goto`允许任意跳转代码位置,可能形成“面条式代码”(spaghetti code),使逻辑混乱,增加调试难度。 2. **可读性差** 频繁使用`goto`会让代码执行路径不直观,尤其是向前跳转(跳过变量初始化)时可能导致未定义行为。 3. **资源泄漏风险** 若跳过资源释放代码(如`free`或`fclose`),会导致内存泄漏或文件句柄未关闭。 4. **违反现代编程规范** 结构化编程提倡使用循环、条件语句和函数调用,而非直接跳转。 --- ### 替代方案示例: #### 场景1:错误处理 **使用`goto`的代码**: ```c void process_file() { FILE *fp = fopen("file.txt", "r"); if (!fp) goto error; char *buffer = malloc(1024); if (!buffer) goto close_file; // 业务逻辑 free(buffer); fclose(fp); return; close_file: fclose(fp); error: printf("Error occurred\n"); } ``` **替代方案(结构化处理)**: ```c void process_file() { FILE *fp = fopen("file.txt", "r"); if (!fp) { printf("Error opening file\n"); return; } char *buffer = malloc(1024); if (!buffer) { fclose(fp); printf("Memory allocation failed\n"); return; } // 业务逻辑 free(buffer); fclose(fp); } ``` #### 场景2:跳出多层循环 **使用`goto`的代码**: ```c for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (condition) goto exit_loops; } } exit_loops: // 后续代码 ``` **替代方案(使用标志变量)**: ```c int flag = 0; for (int i = 0; i < 10 && !flag; i++) { for (int j = 0; j < 10 && !flag; j++) { if (condition) { flag = 1; break; } } } ``` --- ### 合理使用`goto`的例外情况: - **集中清理资源**:在函数内单一出口处统一释放资源(如Linux内核代码风格)。 - **性能关键代码**:极少数需要极致优化的场景(如状态机实现)。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值