代码之家  ›  专栏  ›  技术社区  ›  fizzer

C或C++中好的GOTOS的实例[关闭]

  •  79
  • fizzer  · 技术社区  · 17 年前

    在本文中,我们将看到 goto 在C或C++中。它的灵感来自 an answer 因为他们认为我是在开玩笑,所以他们投票赞成。

    摘要(标签从原来改为更清晰的意思):

    infinite_loop:
    
        // code goes here
    
    goto infinite_loop;
    

    为什么它比其他选择更好:

    • 这是特定的。 古托 是 导致 无条件分支。选择 取决于使用结构 支持条件分支, 一个堕落的人总是对的 条件。
    • 标签记录了意图 没有额外的评论。
    • 读者不必扫描 早期干预代码 break S (尽管仍有可能 没有原则的黑客模拟 continue 早起 古托 )

    规则:

    • 假装哥特人没有 赢。据了解,上述 不能在真正的代码中使用,因为 它违背了既定的习惯用法。
    • 假设我们都听说过 “被认为有害”并知道 那个goto可以用来写 意大利面条代码。
    • 如果你不同意一个例子, 从技术上批评它 因为人们不喜欢 “转到”不是技术原因)。

    让我们看看我们是否能像大人一样谈论这个。

    编辑

    这个问题现在看来已经解决了。它产生了一些高质量的答案。感谢大家, 尤其是那些认真对待我的小循环例子的人。大多数怀疑论者都担心 由于缺少块范围。正如@quinmars在评论中指出的,您可以在 环体。我在通过时注意到 for(;;) while(true) 别给你戴牙套 任何一种都是免费的(并且忽略它们会导致恼人的错误)。不管怎样,我不会再浪费了 你在这件小事上的脑力-我可以忍受无害的和惯用的 (;) 虽然(真) (如果我想保住我的工作也一样)。

    考虑到其他的反应,我看到许多人认为 古托 作为一件事你总是 必须用另一种方式重写。当然你可以避免 古托 通过引入一个循环, 一个额外的标志,一堆嵌套的 if 或者别的什么,但是为什么不考虑 古托 是 也许是这项工作最好的工具?换句话说,为了避免使用内置语言功能达到预期目的,人们准备忍受多少丑陋?我要的是 即使加上一个标志,代价也太高了。我喜欢用变量来表示 问题或解决方案领域。”只是为了避免 古托 “不剪。

    我将接受第一个给出C模式的答案,该模式将分支到一个清理块。在我看来,这是 古托 当然,在所有公布的答案中 如果你用扭曲来衡量它,恨你的人必须通过扭曲来避免它。

    16 回复  |  直到 11 年前
        1
  •  87
  •   Mohamed El-Nakeep    11 年前

    这是我听说过的一个技巧。但我从未在野外见过它。它只适用于C,因为C++有更多的习惯用法。

    void foo()
    {
        if (!doA())
            goto exit;
        if (!doB())
            goto cleanupA;
        if (!doC())
            goto cleanupB;
    
        /* everything has succeeded */
        return;
    
    cleanupB:
        undoB();
    cleanupA:
        undoA();
    exit:
        return;
    }
    
        2
  •  85
  •   Paul Nathan    17 年前

    在C语言中goto的典型需求如下

    for ...
      for ...
        if(breakout_condition) 
          goto final;
    
    final:
    

    没有goto就没有直接的方法可以突破嵌套循环。

        3
  •  32
  •   Mitch Wheat    14 年前

    这是我的一个很好的例子(来自StevensApitue),用于UNIX系统调用,可能会被一个信号中断。

    restart:
        if (system_call() == -1) {
            if (errno == EINTR) goto restart;
    
            // handle real errors
        }
    

    另一种选择是退化循环。这个版本读起来像英语“如果系统调用被信号中断,重新启动它”。

        4
  •  14
  •   Mitch Wheat    17 年前

    如果达夫的设备不需要Goto,那么你也不需要!;)

    void dsend(int count) {
        int n;
        if (!count) return;
        n = (count + 7) / 8;
        switch (count % 8) {
          case 0: do { puts("case 0");
          case 7:      puts("case 7");
          case 6:      puts("case 6");
          case 5:      puts("case 5");
          case 4:      puts("case 4");
          case 3:      puts("case 3");
          case 2:      puts("case 2");
          case 1:      puts("case 1");
                     } while (--n > 0);
        }
    }
    

    维基百科上面的代码 entry .

        5
  •  14
  •   zvrba    17 年前

    Knuth写了一篇论文“用goto语句进行结构化编程”,你可以从 here . 你会发现很多例子。

        6
  •  12
  •   ephemient    17 年前

    非常普遍。

    do_stuff(thingy) {
        lock(thingy);
    
        foo;
        if (foo failed) {
            status = -EFOO;
            goto OUT;
        }
    
        bar;
        if (bar failed) {
            status = -EBAR;
            goto OUT;
        }
    
        do_stuff_to(thingy);
    
    OUT:
        unlock(thingy);
        return status;
    }
    

    我唯一用过的箱子 goto 是为了向前跳,通常是在障碍物外,而不是在障碍物内。这样可以避免滥用 do{}while(0) 以及其他增加嵌套的结构,同时仍然保持可读的结构化代码。

        7
  •  12
  •   Mitch Wheat    14 年前

    一般来说,我并不反对哥特人,但我可以考虑几个原因,为什么你不想把它们用于你提到的循环:

    • 它不限制作用域,因此在内部使用的任何临时变量都将在稍后释放。
    • 它不限制范围,因此可能导致错误。
    • 它不限制作用域,因此以后不能在同一作用域中的代码中重复使用相同的变量名。
    • 它不限制范围,因此您有机会跳过变量声明。
    • 人们不习惯它,它会使您的代码更难阅读。
    • 这种类型的嵌套循环可以导致意大利面条代码,Normals循环不会导致意大利面条代码。
        8
  •  7
  •   Adam Liss    17 年前

    使用goto的一个好地方是在一个可以在多个点中止的过程中,每个点都需要不同级别的清理。gotopobes总是可以用结构化代码和一系列测试来替换goto,但我认为这更简单,因为它消除了过度的缩进:

    if (!openDataFile())
      goto quit;
    
    if (!getDataFromFile())
      goto closeFileAndQuit;
    
    if (!allocateSomeResources)
      goto freeResourcesAndQuit;
    
    // Do more work here....
    
    freeResourcesAndQuit:
       // free resources
    closeFileAndQuit:
       // close file
    quit:
       // quit!
    
        9
  •  7
  •   Mitch Wheat    17 年前

    @fizzer.myopenid.com:您发布的代码片段相当于以下内容:

        while (system_call() == -1)
        {
            if (errno != EINTR)
            {
                // handle real errors
    
                break;
            }
        }
    

    我当然喜欢这种形式。

        10
  •  6
  •   Patrick    17 年前

    尽管随着时间的推移,我越来越讨厌这种模式,但它是以细粒度的方式嵌入到COM编程中的。

    #define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
    ...
    HRESULT SomeMethod(IFoo* pFoo) {
      HRESULT hr = S_OK;
      IfFailGo( pFoo->PerformAction() );
      IfFailGo( pFoo->SomeOtherAction() );
    Error:
      return hr;
    }
    
        11
  •  5
  •   FlySwat    17 年前

    下面是一个很好的goto示例:

    // No Code
    
        12
  •  1
  •   Charles Beattie    14 年前

    我已经看到goto使用正确,但情况很正常,很难看。只有在使用 goto 它本身比原来的差得多。 @约翰森·霍兰德,《波布伦就是你》的版本就不那么清晰了。人们似乎害怕局部变量:

    void foo()
    {
        bool doAsuccess = doA();
        bool doBsuccess = doAsuccess && doB();
        bool doCsuccess = doBsuccess && doC();
    
        if (!doCsuccess)
        {
            if (doBsuccess)
                undoB();
            if (doAsuccess)
                undoA();
        }
    }
    

    我喜欢这样的环,但有些人喜欢 while(true) .

    for (;;)
    {
        //code goes here
    }
    
        13
  •  0
  •   Jim Nelson    17 年前

    我对此感到不满的是,您会丢失块范围;如果循环中断,在goto之间声明的任何局部变量都将保持有效。(也许你假设这个循环永远运行;但我不认为这就是最初的问题编写者所问的。)

    范围的问题是C++的一个问题,因为一些对象可能取决于它们的Dor在适当的时候被调用。

    对我来说,使用goto的最佳原因是在一个多步骤初始化过程中,如果一个初始化过程失败,那么所有init都将退出,这一点非常重要。

    if(!foo_init())
      goto bye;
    
    if(!bar_init())
      goto foo_bye;
    
    if(!xyzzy_init())
      goto bar_bye;
    
    return TRUE;
    
    bar_bye:
     bar_terminate();
    
    foo_bye:
      foo_terminate();
    
    bye:
      return FALSE;
    
        14
  •  0
  •   user25306    17 年前

    我自己不使用Goto,但是我曾经和一个人合作过,在特定的情况下会使用Goto。如果我没记错的话,他的理论基础是关于绩效问题的——他还对 怎样 . 总是在同一个函数中,并且标签总是在goto语句下面。

        15
  •  0
  •   StrifeSephiroth    11 年前
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        char name[64];
        char url[80]; /*The final url name with http://www..com*/
        char *pName;
        int x;
    
        pName = name;
    
        INPUT:
        printf("\nWrite the name of a web page (Without www, http, .com) ");
        gets(name);
    
        for(x=0;x<=(strlen(name));x++)
            if(*(pName+0) == '\0' || *(pName+x) == ' ')
            {
                printf("Name blank or with spaces!");
                getch();
                system("cls");
                goto INPUT;
            }
    
        strcpy(url,"http://www.");
        strcat(url,name);
        strcat(url,".com");
    
        printf("%s",url);
        return(0);
    }
    
        16
  •  -1
  •   FlySwat    17 年前

    @格雷戈:

    为什么不这样做呢:

    void foo()
    {
        if (doA())
        {    
            if (doB())
            {
              if (!doC())
              {
                 UndoA();
                 UndoB();
              }
            }
            else
            {
              UndoA();
            }
        }
        return;
    }