为啥不用 GOTO 语句? [关闭]

Posted

技术标签:

【中文标题】为啥不用 GOTO 语句? [关闭]【英文标题】:Why not GOTO Statement? [closed]为什么不用 GOTO 语句? [关闭] 【发布时间】:2013-11-04 10:54:55 【问题描述】:

我正在攻读软件工程硕士学位。从大学到大学,我从我的老师和导师那里听说过从未在编程语言中使用 GOTO 语句。根据 Software Engineering By D. Sundar

goto 语句的使用使程序变得非结构化,并使其非常 很难理解。

我还在 MICROSOFT 的书第一页上读到了同样的内容,该书从未在编程中使用 GOTO Statmement

我的脑海中总是会出现一个问题,如果我们被指示永远不要使用GOTO Statement,那么why it is the part of many common programming languages

【问题讨论】:

由于人择原理:如果任何语言中都没有goto,您将不会看到任何不使用它的警告。 有时向后兼容,还有一些语言(如 java)根本没有“goto”语句。在某些(非常罕见的)情况下,goto 可能是合适的(您不必担心) goto 实际上在 C 中至关重要,如果您想编写具有多个出口的任意函数。在 C 中避免 goto 的唯一方法是使用非常小的函数和临时清理分支。如果您愿意,C++ 通过析构函数提供“自动gotos”,这就是为什么它允许您编写具有多个存在的任意代码而不会显得混乱。 历史原因。如果您今天从头开始发明一种语言,它就不会有goto。但是在开发 C 的时候,还不是很清楚,C++ 有它是出于 C 兼容性的原因。 why "Using Goto" is bad programming? 的可能重复项 【参考方案1】:

至少对于像 C 和 C++ 这样的一些编程语言(显然我不是其中的设计者),我想它的存在是因为它

    对硬件真正能做的事情进行精确建模,这正是这些语言的目标。 有时实际上比更“结构化”的方法更有用且更好。

goto 通常被认为是好的最常见示例是在 C 中进行嵌套错误/资源管理时。例如,请参阅 Linux 内核,这是一个相当大且成功的 C 项目,它使用 goto 来实现此目的。

【讨论】:

是的,显然我取决于情况,并且比更结构化的方法有用且更好。那为什么我一次又一次地从在从不使用 goto 语句的良好组织中工作的专业人士那里听到它。 ! @MuhammadUsman 因为它通常是有害的,否则更精细的点可能会逃过老师的注意,或者很难向学生解释。 @MuhammadUsman 因为一旦您知道做某事的方法不止一种,即因为您有经验,总会有更好的方法。【参考方案2】:

您想阅读 Edsger W. Dijkstra(1968 年!)的开创性 "Go to statement considered harmful"

【讨论】:

【参考方案3】:

java 中没有 goto 语句。 Java keyword list 指定了 goto 关键字,但它被标记为“未使用”。

【讨论】:

【参考方案4】:

您指示不要使用 goto,因为他们想教您如何编写代码。 但有时 goto 可能很有用。

当您想立即从多级循环中中断时。您可以为每个步骤添加退出条件。你可以使用 goto endloop 看这个例子:(伪代码)

  while(cond1)
    while(cond2)
      while(cond3)
        if(want to break)
          goto endloop
        
        do something 
        if(want to break2)
          goto endloop
        
      
      do something 
    
    do something 
  
endloop:
  do something else

如果没有 goto,它可能看起来像这样:

  while(cond1 && exitloopflag)
    while(cond2 && exitloopflag)
      while(cond3 && exitloopflag)
        if(want to break)
          exitloopflag = true;
          break;
        
        do something 
        if(want to break2)
          exitloopflag = true;
          break;
        
      
      if(exitloopflag)
        break;
      do something     
    
    if(exitloopflag)
      break;
    do something 
  
  do something else

所以我们可以争论哪个代码可读性更好......

【讨论】:

【参考方案5】:

goto 在任何现代编程语言中的存在很大程度上都是退化的,有点像人类的附录。它的功能已被条件和循环控制结构(if-then-else、for/while/loops、switch/case 语句等)取代。它存在于 C 和 C++ 等语言中,因为在某些极端情况下它仍然非常有用,例如打破深度嵌套的循环:

for (...)

  for (...)
  
     for(...)
     
        ...
        // hit a fatal error, need to break out to outermost scope
        goto whoopsiedoodle;
     
  

whoopsiedoodle: 
...

但是,不鼓励使用它有几个很好的理由:因为它可以在函数中分支任意方向,它可以破坏通过简单检查来调试代码的能力。例如,给定以下 sn-p:

       i = 1;
label: printf("i = %d\n", i);

i 打印什么值?该打印语句将执行多少次?直到您考虑了goto label; 的每个实例,您才能知道。现在,想象一下代码的结构如下:

       i = 0;
       goto label;
foo:   ...
       ...
       i = 1;
label: printf("i = %d\n", i);
       ...
       goto foo;
       ...

现在,想象一下上面 sn-p 中每个 ... 的几十行(甚至几百行)代码。还可以想象散布在各处的 10 或 11 个其他标签,相关的 goto 语句大部分随机分布。这是根据我在职业生涯早期遇到的一些真实代码建模的。像这样调试代码的唯一方法是逐行跟踪执行,并在此过程中计算每个goto。这段代码一开始就写得很糟糕(一个单一的、5000 行的 main 函数,字面上是 数百 个单独的变量,一些在文件范围内声明,一些在 main 本地,以及其他暴行) ,但是goto 的使用是一个力量放大器,它把糟糕的代码变成了无法维护的污泥。此代码定义“脆”;我们真的无法在不破坏其他内容的情况下更改其中的一行。

过度使用goto 也会妨碍编译器优化代码的能力。现代编译器非常聪明,可以利用结构化编程技术进行有效的分支预测或展开循环以获得真正的性能提升。编译器几乎不可能优化上述代码。上面的 sn-p 模拟的真实世界代码?我们尝试在打开优化标志的情况下编译它(gcc -O1)。编译器吃掉了所有可用的 RAM,然后吃掉了所有可用的交换空间,导致内核恐慌。

我们告诉客户,他们要么需要购买更快的硬件,要么允许我们从头开始重写整个事情。他们最终购买了更快的硬件。

goto可以有效使用,只要遵守以下规则:

    向前分支。 永远不要分支到控制结构体中(即不要绕过ifforwhileswitch 条件)。 避免在大段代码上进行分支。

【讨论】:

【参考方案6】:

我想解释一下,为什么要非常小心地使用 GOTO。

我并不是说 GOTO 是一个不好的陈述,这就是为什么它仍然在 C++ 中实现的原因之一(并且由于与 C 的兼容性原因),在 JAVA 中它仅保留为关键字。

我可以向您展示 GOTO 是最佳解决方案的示例(在我看来)

但你应该避免使用它,原因如下:

(由我自己翻译和修改,来自本书Ohne C Zu C++)

想象一下,你在玩像《卡坦岛定居者》这样的游戏,无论你走到哪里,每个人都有自己的 规则。 没关系,你只需要学习规则,然后你就可以玩它们了。

但是如果他们不教你规则,而只是使用它呢?你会失去游戏的乐趣 他们非常快。

goto 语句就像新的 Settler of Catan 规则。

当您可以可靠地假设您从顶部开始,一次执行 1 行,向下,一次执行一行时,编程很难理解。

使用 goto 语句会抛出该假设,突然之间,“游戏!是不确定的。这是一个新的且不确定的规则。

【讨论】:

【参考方案7】:

在某些语言中,它用于异常处理(例如 VBA)

Java 有 goto 作为 keyword 但它什么也没做。我相信他们这样做了,你不能说出任何名字goto

但是,在某些情况下,goto 可能很有用!

【讨论】:

【参考方案8】:

goto 在 Java 中是明确不允许的。它存在于其他语言中,因为它很容易实现,因为它是机器代码中的简单指令,而不是因为它是一个好主意。

顺便说一句,您可以在 Java 中执行类似 goto 的操作。看看你是否可以很容易地弄清楚这个简单的例子做了什么,如果你不能,你就是在回答你自己的问题。如果您很清楚它的作用和工作原理,那么您必须考虑到很多人会觉得这很混乱。

FOUND: 
    for(String s: list)
        if(s.contains(like))
             break FOUND;
    System.out.println(like + " not found");

【讨论】:

【参考方案9】:

Goto 语句模型机器码;机器代码中的流控制结构非常有限,如果没有 goto 等价物,就无法编写重要的机器代码程序。

最初的高级语言(例如 COBOL 和 FORTRAN)具有 Goto,因为人们知道如何通过在机器上执行机器代码来控制他们的流程。 C 语言也是用一种语言创建的,尽管几年后,部分原因是该语言是专门为易于编译为高效的机器代码而创建的——一些 C 语言结构,例如递增/递减运算符,之所以存在是因为最初为其创建 C 的机器在其机器代码中具有这些子操作。可以说 C 不是高级语言,它是一种结构化的汇编程序(这并没有什么问题,它领先于时代,仍然非常有用)。

但是,Dijkstra 和其他人发现,(1) 如果您有足够的高级结构来进行流控制,则不需要 goto,并且 (2) 它极易出错。我记得读过一些代码分析,发现 goto 语句出错的可能性是任何其他类型语句的 9 倍。

具有讽刺意味的是,从概念上理解语句在做什么并不难;只是程序员对它的使用过于频繁,导致整个程序更难理解。

【讨论】:

【参考方案10】:

Goto 在我们跳跃时是允许的

超出范围, 就在示波器的后面。

所有其他情况都是无效的(跳入循环,跳入 if 块的中间,跳入另一个函数,OMG,万圣节快乐)。

由于有多种方法可以打破范围,如breakbreak labelreturn等,因此没有纯goto 必须使用(除了一些早期的语言不支持上面的一些,通常是break label)。

换句话说,goto并没有消失(不改变执行顺序是不可能写出程序的),我们只是针对不同类型的执行路径使用不同的关键字进行区分。 Goto 现在有特殊情况,它们有自己的关键字。它还使代码更易于理解。

【讨论】:

以上是关于为啥不用 GOTO 语句? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

goto语句为啥不受待见

为啥 switch 语句不向应用返回信息? [关闭]

为啥这个 if 语句在这种情况下不起作用? [关闭]

为啥不使用嵌套的 if 语句有啥具体原因吗? [关闭]

为啥我应该使用 continue 而不是空的 if 语句? [关闭]

为啥我不能在“if”语句中与 '_' 字符进行比较? [关闭]