当前位置:  开发笔记 > 编程语言 > 正文

C或C++中良好的getos的例子

如何解决《C或C++中良好的getos的例子》经验,为你挑选了11个好方法。

在这个主题中,我们看一下gotoC或C++ 的良好用法示例.它的灵感来自人们投票的答案,因为他们认为我在开玩笑.

摘要(标签从原始标签更改为使意图更清晰):

infinite_loop:

    // code goes here

goto infinite_loop;

为什么它比替代品更好:

这是具体的. goto是导致无条件分支的语言构造.替代方案取决于使用支持条件分支的结构,具有退化的始终为真的条件.

标签记录了意图,没有额外的评论.

读者不必扫描早期介入代码breakS(尽管它仍然可能无原则黑客模拟 continue与早期goto).

规则:

假装gotophobes没有赢.据了解,上述内容不能用于实际代码中,因为它违背了既定惯用语.

假设我们都听说'Goto被认为有害',并且知道goto可以用来写意大利面条代码.

如果你不同意一个例子,那就单独批评技术优点('因为人们不喜欢goto'不是技术原因).

让我们看看我们是否可以像成年人一样谈论这个问题.

编辑

这个问题现在好了.它产生了一些高质量的答案.感谢大家,尤其是那些认真对待我的小循环示例的人.大多数怀疑论者都担心缺乏范围.正如@quinmars在评论中指出的那样,你总是可以在循环体上放置大括号.我顺便注意到for(;;)并且while(true)也没有给你免费的大括号(并且省略它们会导致烦恼的错误).无论如何,我不会浪费任何更多的脑力这个小事-我可以用无害的习惯生活for(;;)while(true)(一样好,如果我想继续我的工作).

考虑到其他反应,我发现许多人认为goto你总是需要以另一种方式重写.当然,你可以goto通过引入一个循环,一个额外的标志,一堆嵌套的ifs或其他什么来避免,但为什么不考虑是否goto可能是这项工作的最佳工具?换句话说,人们准备忍受多少丑陋以避免使用内置语言功能达到预期目的?我的看法是,即使添加一面旗帜,也要付出太高的代价.我喜欢我的变量来表示问题或解决方案域中的事物.'唯一要避免goto'不削减它.

我会接受第一个给出C模式分支到清理块的答案.国际海事组织,这是goto所有已发布答案的最强情况,当然,如果你通过一个仇恨必须通过避免它的扭曲来衡量它.



1> Greg Rogers..:

这是我听说人们使用的一个技巧.我从来没有在野外见过它.它只适用于C,因为C++有RAII来更加惯用.

void foo()
{
    if (!doA())
        goto exit;
    if (!doB())
        goto cleanupA;
    if (!doC())
        goto cleanupB;

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}


你可以在大多数低级Unix事物中看到很多这样的代码(比如linux内核).在C中,这是错误恢复恕我直言的最佳习惯用法.
正如cournape所提到的,Linux内核始终使用这种风格*.
不仅linux内核看看微软的Windows驱动程序样本,你会发现相同的模式.一般来说,这是一种处理异常的C方式,非常有用:).我通常只选择1个标签,在极少数情况下,在99%的情况下可以避免使用2个标签.
我*知道*我应该在高中学习"速度打字"课程!好答案.
额外的块会导致难以阅读的不必要的缩进.如果其中一个条件在循环内,它也不起作用.
这有什么问题?(除了注释不理解换行符之外)void foo(){if(doA()){if(doB()){if(doC()){/*一切都成功*/return; } undoB(); } undoA(); } return; }

2> Paul Nathan..:

C中GOTO的经典需求如下

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

final:

没有goto,没有直接的方法来突破嵌套循环.


大多数情况下,当需要出现时,我可以使用返回.但是你需要为它编写小函数来解决这个问题.
@Adam Rosenfield:由Dijkstra强调的带有goto的*整个问题*是结构化编程提供了更易理解的结构.使代码更难以阅读以避免goto证明作者未能理解该论文...
@metao:Bjarne Stroustrup不同意.在他的C++编程语言书中,这正是goto"很好用"的例子.
我绝对同意Darius - 将它重构为一个函数并返回!
@Steve Jessop:人们不仅误解了这篇文章,大多数"不去"的邪教徒都不理解写作的历史背景.
要添加到这个:没有goto的方法是设置一个布尔标志,并测试该标志每个外部循环的每次迭代.这会产生不太可读的代码; 它的效率也略低,但这只与极少数情况有关(例如内核代码).
为什么你会在该代码中有最终结果?把它分解成一个函数.
@DariusBacon实际上,返回并不总是一种享受,因为如果您例如已经分配了内存或其他资源,您可能希望在返回之前释放它们,那就是goto很棒的地方.
我95%肯定`return`语句会生成一个跳转到函数末尾的返回(假设你有一个堆栈帧崩溃),所以它与使用`goto`除了源之外没什么不同可读性.但是,就像有人指出的那样,`goto`是一个四个字母的单词......;)

3> fizzer..:

这是我的非愚蠢的例子(来自Stevens APITUE),用于Unix系统调用,可能会被信号中断.

restart:
    if (system_call() == -1) {
        if (errno == EINTR) goto restart;

        // handle real errors
    }

替代方案是简并循环.这个版本读起来像英语"如果系统调用被信号中断,重新启动它".


@Amarghosh,`continue`只是一个面具中的'goto`.
这种方式用于例如linux调度程序有这样的goto但我会说很少有情况下,后向goto是可以接受的,一般应该避免.
@fizzer oopsie ..它需要两个'else break`s(每个ifs一个)才能使它等同......我能说什么...`goto`或不,你的代码只和你一样好:(
@jball ...嗯..为了争论,是的; 你可以使用goto和spaghetti代码继续制作好的可读代码.最终它取决于编写代码的人.关键是很容易迷失goto而不是继续.新手通常使用他们为每个问题获得的第一把锤子.
@Amarghosh.您的"改进"代码甚至不等同于原始代码 - 它在成功案例中永远循环.

4> Mitch Wheat..:

如果Duff的设备不需要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);
    }
}

维基百科条目上面的代码.


幽默经常在这里不受重视......
这是一个逻辑谬误的例子,也被称为"对权威的诉求".在维基百科上查看.
duff的设备能酿啤酒吗?
这个可以一次8个;-)
Cmon们,如果你打算投票,那么简短评论为什么会很棒.:)
我不会聘请一个无幽默的程序员.

5> zvrba..:

Knuth写了一篇论文"使用GOTO语句进行结构化编程",你可以从这里得到它.你会在那里找到很多例子.


我很失望你没有写::你可以GOTO'在这里'得到它,对我的双关语将是难以忍受的不做!
哪些假设?今天他的例子对于像C这样的程序语言(他用一些伪代码给出它们)就像他们当时一样.
那篇论文*过时了*,甚至都不好笑.Knuth的假设根本不再存在.

6> Brian R. Bon..:

我一般都没有反对任何东西,但我可以想到几个原因,为什么你不想像你提到的那样将它们用于循环:

它不限制范围,因此您在内部使用的任何临时变量都不会在以后释放.

它不限制范围因此可能导致错误.

它不限制范围,因此您不能在以后的代码中重复使用相同的变量名称.

它不限制范围,因此您有机会跳过变量声明.

人们不习惯它,它会使你的代码更难阅读.

这种类型的嵌套循环可能导致意大利面条代码,法线循环不会导致意大利面条代码.


对于这个例子,即使没有括号,点1-4也是可疑的*.1这是一个无限循环.2太模糊了.3尝试在封闭范围内隐藏同名变量是不好的做法.4.向后转到不能跳过声明.

7> ephemient..:

很普通的.

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)和其他增加嵌套的构造,同时仍然保持可读的结构化代码.


我认为这是错误处理的典型C方式.我无法看到它被很好地替换,更好的可读性任何其他方式问候

8> Adam Liss..:

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

if (!openDataFile())
  goto quit;

if (!getDataFromFile())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!


如果您创建了这3个额外函数,则需要将指针/句柄传递给要释放/关闭的资源和文件.你可能会使freeResourcesCloseFileQuit()调用closeFileQuit(),然后调用quit().现在你有4个紧密耦合的函数来维护,其中3个可能最多被调用一次:来自上面的单个函数.如果你坚持避免使用goto,IMO,嵌套的if()块具有更少的开销并且更易于阅读和维护.你有3个额外功能可以获得什么?

9> Mitch Wheat..:

@ fizzer.myopenid.com:您发布的代码段等效于以下内容:

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

            break;
        }
    }

我绝对喜欢这种形式.


在我看来,它令人困惑.这是一个你不希望进入正常执行路径的循环,因此逻辑似乎是倒退的.但是,意见很重要.
我同意这里的fizzer,goto对条件的价值提供了更清晰的期望.
这个片段在哪种方式比fizzer更容易阅读.

10> JaredPar..:

尽管随着时间的推移我已经开始讨厌这种模式,但它已经融入了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> FlySwat..:

这是一个很好的转到的例子:

// No Code


这不是真的有用
"没有转到你的身上!"
呃,没有吗?(不成功的搞笑:d)
推荐阅读
LEEstarmmmmm
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有