GOTO 仍然被视为有害吗?

发布于 2024-07-04 10:22:39 字数 1448 浏览 9 评论 0 原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

沫雨熙 2024-07-11 10:22:40

现代 GOTO 的一种用法是由 C# 编译器为由收益返回定义的枚举器创建状态机。

GOTO 应该由编译器而不是程序员使用。

One modern GOTO usage is by the C# compiler to create state machines for enumerators defined by yield return.

GOTO is something that should be used by compilers and not programmers.

幸福不弃 2024-07-11 10:22:40

我避免使用它,因为同事/经理无疑会在代码审查中或偶然发现它时质疑它的使用。 虽然我认为它有用途(例如错误处理情况),但您会与其他遇到某种问题的开发人员发生冲突。

这不值得。

I avoid it since a coworker/manager will undoubtedly question its use either in a code review or when they stumble across it. While I think it has uses (the error handling case for example) - you'll run afoul of some other developer who will have some type of problem with it.

It’s not worth it.

盗心人 2024-07-11 10:22:40

几乎在所有可以使用 goto 的情况下,您都可以使用其他构造来执行相同的操作。 无论如何,编译器都会使用 Goto。

我个人从不明确使用它,也不需要。

Almost all situations where a goto can be used, you can do the same using other constructs. Goto is used by the compiler anyway.

I personally never use it explicitly, don't ever need to.

腹黑女流氓 2024-07-11 10:22:40

您可以使用它来打破深度嵌套循环,但大多数时候您的代码可以重构为更干净,而无需深度嵌套循环。

You can use it for breaking from a deeply nested loop, but most of the time your code can be refactored to be cleaner without deeply nested loops.

躲猫猫 2024-07-11 10:22:40

XKCD 的 GOTO 漫画

我的一位同事说,使用 GOTO 的唯一原因是,如果你将自己编程到了一个角落,这是唯一的出路。 换句话说,提前正确设计,以后就不需要使用 GOTO。

我认为这部漫画完美地说明了“我可以重组程序的流程,或者使用一个小‘GOTO’来代替。” 当设计薄弱时,GOTO 是一种薄弱的出路。 迅猛龙捕食弱者

XKCD's GOTO Comic

A coworker of mine said the only reason to use a GOTO is if you programmed yourself so far into a corner that it is the only way out. In other words, proper design ahead of time and you won't need to use a GOTO later.

I thought this comic illustrates that beautifully "I could restructure the program's flow, or use one little 'GOTO' instead." A GOTO is a weak way out when you have weak design. Velociraptors prey on the weak.

莫言歌 2024-07-11 10:22:40

Donald E. Knuth 在《文学编程》一书中回答了这个问题,1992 年 CSLI。 上页。 17 有一篇文章“使用 goto 语句进行结构化编程 “(PDF)。 我认为这篇文章也可能发表在其他书中。

文章描述了 Dijkstra 的建议并描述了该建议有效的情况。 但他还给出了许多反例(问题和算法),仅使用结构化循环无法轻松重现这些反例。

这篇文章包含了问题的完整描述、历史、例子和反例。

Donald E. Knuth answered this question in the book "Literate Programming", 1992 CSLI. On p. 17 there is an essay "Structured Programming with goto Statements" (PDF). I think the article might have been published in other books as well.

The article describes Dijkstra's suggestion and describes the circumstances where this is valid. But he also gives a number of counter examples (problems and algorithms) which cannot be easily reproduced using structured loops only.

The article contains a complete description of the problem, the history, examples and counter examples.

痞味浪人 2024-07-11 10:22:40

在 C 中,goto 仅在当前函数的范围内工作,这往往会本地化任何潜在的错误。 setjmplongjmp 危险得多,它们是非本地的、复杂的并且依赖于实现。 然而在实践中,它们太晦涩和不常见,不会引起很多问题。

我认为 C 语言中 goto 的危险被过分夸大了。 请记住,最初的 goto 参数出现在老式 BASIC 等语言时代,初学者会编写如下所示的意大利面条代码:

3420 IF A > 2 THEN GOTO 1430

这里 Linus 描述了 goto 的适当用法>: http://www.kernel.org/doc/Documentation/CodingStyle(章节7).

In C, goto only works within the scope of the current function, which tends to localise any potential bugs. setjmp and longjmp are far more dangerous, being non-local, complicated and implementation-dependent. In practice however, they're too obscure and uncommon to cause many problems.

I believe that the danger of goto in C is greatly exaggerated. Remember that the original goto arguments took place back in the days of languages like old-fashioned BASIC, where beginners would write spaghetti code like this:

3420 IF A > 2 THEN GOTO 1430

Here Linus describes an appropriate use of goto: http://www.kernel.org/doc/Documentation/CodingStyle (chapter 7).

爱你是孤单的心事 2024-07-11 10:22:40

在某些情况下,Go To 可以提供一种“真正的”异常处理的替代方法。 考虑一下:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

显然,这段代码经过了简化,占用了更少的空间,所以不要太在意细节。 但考虑一下我在生产代码中多次看到的另一种选择,编码人员为了避免使用 goto 而采取了荒谬的长度:

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

现在从功能上讲,这段代码做了完全相同的事情。 事实上,编译器生成的代码几乎是相同的。 然而,在程序员热衷于安抚Nogoto(可怕的学术斥责之神)的过程中,这位程序员完全打破了while循环所代表的底层习惯用法,并做了真正的代码可读性的数字。 这并没有更好。

所以,这个故事的寓意是,如果您发现自己为了避免使用 goto 而采取了一些非常愚蠢的做法,那就不要这样做。

Go To can provide a sort of stand-in for "real" exception handling in certain cases. Consider:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

Obviously this code was simplified to take up less space, so don't get too hung up on the details. But consider an alternative I've seen all too many times in production code by coders going to absurd lengths to avoid using goto:

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

Now functionally this code does the exact same thing. In fact, the code generated by the compiler is nearly identical. However, in the programmer's zeal to appease Nogoto (the dreaded god of academic rebuke), this programmer has completely broken the underlying idiom that the while loop represents, and did a real number on the readability of the code. This is not better.

So, the moral of the story is, if you find yourself resorting to something really stupid in order to avoid using goto, then don't.

清秋悲枫 2024-07-11 10:22:40

被 Jay Ballou 添加答案所吸引,我将添加 0.02 英镑。 如果 Bruno Ranschaert 还没有这样做,我会提到 Knuth 的“使用 GOTO 语句进行结构化编程”文章。

我没有看到讨论的一件事是 Fortran 教科书上教授的那种代码,虽然并不常见,但却是这样的。 例如 DO 循环的扩展范围和开放编码的子例程(请记住,这将是 Fortran II、Fortran IV 或 Fortran 66 - 而不是 Fortran 77 或 90)。 至少有可能语法细节不准确,但概念应该足够准确。 每种情况下的片段都位于单个函数内。

请注意,优秀但已过时(且已绝版)的书“编程风格的元素,第二Edn',作者:Kernighan & Plauger 列举了当时(70 年代末)编程教科书中一些滥用 GOTO 的现实例子。 然而,下面的材料并非来自该书。

DO 循环的扩展范围

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

造成这种废话的原因之一是老式的打孔卡。 您可能会注意到,标签(完全不按顺序排列,因为这是规范风格!)位于第 1 列(实际上,它们必须位于第 1-5 列),代码位于第 7-72 列(第 6 列是延续)标记列)。 第 73-80 列将被赋予一个序列号,并且有机器可以将打孔卡组按序列号顺序进行排序。 如果您的程序位于已排序的卡片上,并且需要在循环中间添加一些卡片(行),则必须在这些额外的行之后重新打孔所有内容。 但是,如果您用 GOTO 内容替换一张卡片,则可以避免对所有卡片重新排序 - 您只需将新卡片用新的序列号塞到例程末尾即可。 可以将其视为“绿色计算”的首次尝试 - 节省打孔卡(或者更具体地说,节省重新打字的劳动力 - 以及节省随之而来的重新键入错误)。

哦,你可能还注意到我在作弊,而不是大喊大叫——Fortran IV 通常都是用大写字母编写的。

开放编码子例程

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

标签 76 和 54 之间的 GOTO 是计算 goto 的一个版本。 如果变量 i 的值为 1,则转到列表中的第一个标签 (123); 如果它的值为 2,则转到第二个,依此类推。 从 76 到计算出的 goto 的片段是开放编码的子例程。 它是一段类似于子例程的代码,但写在函数体中。 (Fortran 也有语句函数 - 它们是适合单行的嵌入式函数。)

有比计算 goto 更糟糕的结构 - 您可以为变量分配标签,然后使用指定的 goto。 谷歌搜索 分配的 goto 告诉我它已从 Fortran 95 中删除。为结构化编程革命做好准备,这场革命可以说是从 Dijkstra 的“GOTO 被认为有害”的信件或文章开始的。

如果不了解 Fortran 中所做的事情(以及其他语言中的事情,其中​​大多数已经被抛在一边),我们这些新手很难理解 Dijkstra 正在处理的问题的范围。 哎呀,直到那封信发表十年后我才开始编程(但我确实不幸使用 Fortran IV 编程了一段时间)。

Attracted by Jay Ballou adding an answer, I'll add my £0.02. If Bruno Ranschaert had not already done so, I'd have mentioned Knuth's "Structured Programming with GOTO Statements" article.

One thing that I've not seen discussed is the sort of code that, while not exactly common, was taught in Fortran text books. Things like the extended range of a DO loop and open-coded subroutines (remember, this would be Fortran II, or Fortran IV, or Fortran 66 - not Fortran 77 or 90). There's at least a chance that the syntactic details are inexact, but the concepts should be accurate enough. The snippets in each case are inside a single function.

Note that the excellent but dated (and out of print) book 'The Elements of Programming Style, 2nd Edn' by Kernighan & Plauger includes some real-life examples of abuse of GOTO from programming text books of its era (late-70s). The material below is not from that book, however.

Extended range for a DO loop

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

One reason for such nonsense was the good old-fashioned punch-card. You might notice that the labels (nicely out of sequence because that was canonical style!) are in column 1 (actually, they had to be in columns 1-5) and the code is in columns 7-72 (column 6 was the continuation marker column). Columns 73-80 would be given a sequence number, and there were machines that would sort punch card decks into sequence number order. If you had your program on sequenced cards and needed to add a few cards (lines) into the middle of a loop, you'd have to repunch everything after those extra lines. However, if you replaced one card with the GOTO stuff, you could avoid resequencing all the cards - you just tucked the new cards at the end of the routine with new sequence numbers. Consider it to be the first attempt at 'green computing' - a saving of punch cards (or, more specifically, a saving of retyping labour - and a saving of consequential rekeying errors).

Oh, you might also note that I'm cheating and not shouting - Fortran IV was written in all upper-case normally.

Open-coded subroutine

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

The GOTO between labels 76 and 54 is a version of computed goto. If the variable i has the value 1, goto the first label in the list (123); if it has the value 2, goto the second, and so on. The fragment from 76 to the computed goto is the open-coded subroutine. It was a piece of code executed rather like a subroutine, but written out in the body of a function. (Fortran also had statement functions - which were embedded functions that fitted on a single line.)

There were worse constructs than the computed goto - you could assign labels to variables and then use an assigned goto. Googling assigned goto tells me it was deleted from Fortran 95. Chalk one up for the structured programming revolution which could fairly be said to have started in public with Dijkstra's "GOTO Considered Harmful" letter or article.

Without some knowledge of the sorts of things that were done in Fortran (and in other languages, most of which have rightly fallen by the wayside), it is hard for us newcomers to understand the scope of the problem which Dijkstra was dealing with. Heck, I didn't start programming until ten years after that letter was published (but I did have the misfortune to program in Fortran IV for a while).

尴尬癌患者 2024-07-11 10:22:40

后藤认为很有帮助。

我从 1975 年开始编程。对于 20 世纪 70 年代的程序员来说,“goto 被认为是有害的”这句话或多或少表明具有现代控制结构的新编程语言值得尝试。 我们确实尝试了新语言。 我们很快就转变了。 我们再也没有回去过。

我们再也没有回去过,但是,如果你还年轻,那么你一开始就从来没有去过那里。

现在,古代编程语言的背景可能没有多大用处,除非可以作为程序员年龄的指标。 尽管如此,年轻的程序员缺乏这种背景,因此他们不再理解“goto 被认为有害”这一口号在推出时向其目标受众传达的信息。

不理解的口号并不具有启发性。 。 也许最好忘记这些口号。 这样的口号没有帮助。

然而,“后藤被认为是有害的”这个特殊的口号已经拥有了它自己的不死生命。

goto能不被滥用吗? 答案:当然可以,但是那又怎样呢? 实际上,每个编程元素都可能被滥用。 例如,不起眼的 bool 被滥用的次数比我们中一些人想象的要多。

相比之下,自 1990 年以来,我不记得遇到过任何实际的 goto 滥用实例。goto

的最大问题可能不是技术问题,而是社会问题。 不太了解的程序员有时似乎觉得弃用 goto 让他们听起来很聪明。 您可能有时必须满足这样的程序员。 这就是人生。

今天goto最糟糕的地方是它没有被充分利用。

Goto considered helpful.

I started programming in 1975. To 1970s-era programmers, the words "goto considered harmful" said more or less that new programming languages with modern control structures were worth trying. We did try the new languages. We quickly converted. We never went back.

We never went back, but, if you are younger, then you have never been there in the first place.

Now, a background in ancient programming languages may not be very useful except as an indicator of the programmer's age. Notwithstanding, younger programmers lack this background, so they no longer understand the message the slogan "goto considered harmful" conveyed to its intended audience at the time it was introduced.

Slogans one does not understand are not very illuminating. It is probably best to forget such slogans. Such slogans do not help.

This particular slogan however, "Goto considered harmful," has taken on an undead life of its own.

Can goto not be abused? Answer: sure, but so what? Practically every programming element can be abused. The humble bool for example is abused more often than some of us would like to believe.

By contrast, I cannot remember meeting a single, actual instance of goto abuse since 1990.

The biggest problem with goto is probably not technical but social. Programmers who do not know very much sometimes seem to feel that deprecating goto makes them sound smart. You might have to satisfy such programmers from time to time. Such is life.

The worst thing about goto today is that it is not used enough.

风向决定发型 2024-07-11 10:22:40

原始论文应该被认为是“无条件 GOTO 被认为是有害的”。 它特别提倡一种基于条件 (if) 和迭代 (while) 结构的编程形式,而不是早期代码中常见的测试和跳转。 goto 在某些语言或不存在适当控制结构的情况下仍然有用。

The original paper should be thought of as "Unconditional GOTO Considered Harmful". It was in particular advocating a form of programming based on conditional (if) and iterative (while) constructs, rather than the test-and-jump common to early code. goto is still useful in some languages or circumstances, where no appropriate control structure exists.

治碍 2024-07-11 10:22:40

实际上,我发现自己被迫使用 goto,因为我确实想不出更好(更快)的方法来编写这段代码:

我有一个复杂的对象,我需要对其进行一些操作。 如果对象处于一种状态,那么我可以执行快速版本的操作,否则我必须执行慢速版本的操作。 问题是,在某些情况下,在慢速操作的中间,有可能意识到这可以通过快速操作来完成。

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

这是实时 UI 代码中速度关键的一段,所以我真的认为 GOTO 在这里是合理的。

雨果

I actually found myself forced to use a goto, because I literally couldn't think of a better (faster) way to write this code:

I had a complex object, and I needed to do some operation on it. If the object was in one state, then I could do a quick version of the operation, otherwise I had to do a slow version of the operation. The thing was that in some cases, in the middle of the slow operation, it was possible to realise that this could have been done with the fast operation.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

This was in a speed critical piece of realtime UI code, so I honestly think that a GOTO was justified here.

Hugo

一抹微笑 2024-07-11 10:22:40

Linux 中:在内核代码中使用 goto 在 Kernel Trap 上,与 Linus Torvalds 和一个“新人”讨论了在 Linux 代码中使用 GOTO。 其中有一些非常好的观点,Linus 也表现出了一贯的傲慢态度:)

一些段落:

Linus:“不,你已经被洗脑了
CS人认为尼克劳斯
沃斯实际上知道他是什么
谈论。 他没有。 他不
有一条令人发指的线索。”

-

Linus:“我认为 goto 很好,而且
它们通常比
大量的缩进。”

-

Linus:“当然,用愚蠢的语言
就像帕斯卡一样,标签不能
描述性的,goto 可能很糟糕。”

In Linux: Using goto In Kernel Code on Kernel Trap, there's a discussion with Linus Torvalds and a "new guy" about the use of GOTOs in Linux code. There are some very good points there and Linus dressed in that usual arrogance :)

Some passages:

Linus: "No, you've been brainwashed by
CS people who thought that Niklaus
Wirth actually knew what he was
talking about. He didn't. He doesn't
have a frigging clue."

-

Linus: "I think goto's are fine, and
they are often more readable than
large amounts of indentation."

-

Linus: "Of course, in stupid languages
like Pascal, where labels cannot be
descriptive, goto's can be bad."

池木 2024-07-11 10:22:40

我从这里的任何答案中都没有看到的一件事是,“转到”解决方案通常比经常提到的结构化编程解决方案之一更有效

考虑许多嵌套循环的情况,其中使用“goto”而不是一堆 if(breakVariable) 部分显然更有效。 “将循环放入函数并使用 return”的解决方案通常是完全不合理的。 在循环使用局部变量的情况下,您现在必须通过函数参数将它们全部传递,可能会处理由此产生的大量额外麻烦。

现在考虑清理案例,我自己经常使用它,而且很常见,可能是造成许多语言中不可用的 try{} catch {} 结构的原因。 完成同一件事所需的检查和额外变量的数量远比进行跳转的一两条指令要差得多,而且,附加函数解决方案根本不是解决方案。 你不能告诉我这更易于管理或更具可读性。

现在,在许多情况下,代码空间、堆栈使用和执行时间对于许多程序员来说可能并不重要,但是当您处于只有 2KB 代码空间可供使用的嵌入式环境中时,需要 50 字节的额外指令来避免明确定义的指令“goto”只是可笑的,而且这种情况并不像许多高级程序员认为的那么罕见。

“goto 是有害的”这一说法对于转向结构化编程非常有帮助,即使它总是过于概括。 至此,我们都已经听过它了,因此对使用它持谨慎态度(我们应该如此)。 当它显然是适合工作的工具时,我们不需要害怕它。

One thing I've not seen from any of the answers here is that a 'goto' solution is often more efficient than one of the structured programming solutions often mentioned.

Consider the many-nested-loops case, where using 'goto' instead of a bunch of if(breakVariable) sections is obviously more efficient. The solution "Put your loops in a function and use return" is often totally unreasonable. In the likely case that the loops are using local variables, you now have to pass them all through function parameters, potentially handling loads of extra headaches that arise from that.

Now consider the cleanup case, which I've used myself quite often, and is so common as to have presumably been responsible for the try{} catch {} structure not available in many languages. The number of checks and extra variables that are required to accomplish the same thing are far worse than the one or two instructions to make the jump, and again, the additional function solution is not a solution at all. You can't tell me that's more manageable or more readable.

Now code space, stack usage, and execution time may not matter enough in many situations to many programmers, but when you're in an embedded environment with only 2KB of code space to work with, 50 bytes of extra instructions to avoid one clearly defined 'goto' is just laughable, and this is not as rare a situation as many high-level programmers believe.

The statement that 'goto is harmful' was very helpful in moving towards structured programming, even if it was always an over-generalization. At this point, we've all heard it enough to be wary of using it (as we should). When it's obviously the right tool for the job, we don't need to be scared of it.

残月升风 2024-07-11 10:22:40

“在此链接中http://kerneltrap.org/node/553/2131

具有讽刺意味的是,消除goto 引入了一个错误:省略了自旋锁调用。

"In this link http://kerneltrap.org/node/553/2131"

Ironically, eliminating the goto introduced a bug: the spinlock call was omitted.

独闯女儿国 2024-07-11 10:22:40

我同意 Goto 唯一可以使用的地方是当您需要处理错误时,并且发生错误的每个特定点都需要特殊处理。

例如,如果您正在获取资源并使用信号量或互斥体,则必须按顺序获取它们,并且应该始终以相反的方式释放它们。

有些代码需要一种非常奇怪的模式来获取这些资源,并且您不能只编写一个易于维护和理解的控制结构来正确处理这些资源的获取和释放以避免死锁。

不使用 goto 总是可以正确完成任务,但在这种情况下以及其他一些情况下,Goto 实际上是更好的解决方案,主要是为了可读性和可维护性。

-亚当

About the only place I agree Goto could be used is when you need to deal with errors, and each particular point an error occurs requires special handling.

For instance, if you're grabbing resources and using semaphores or mutexes, you have to grab them in order and you should always release them in the opposite manner.

Some code requires a very odd pattern of grabbing these resources, and you can't just write an easily maintained and understood control structure to correctly handle both the grabbing and releasing of these resources to avoid deadlock.

It's always possible to do it right without goto, but in this case and a few others Goto is actually the better solution primarily for readability and maintainability.

-Adam

巨坚强 2024-07-11 10:22:40

拒绝程序员使用 GOTO 语句就像告诉木匠不要使用锤子,因为当他敲钉子时可能会损坏墙壁。 真正的程序员知道如何以及何时使用 GOTO。 我跟踪了一些所谓的“结构化程序”,我看到了这样可怕的代码,只是为了避免使用 GOTO,这样我就可以射击程序员。 好吧,为了保护另一方,我一次又一次地看到了一些真正的意大利面条代码,那些程序员也应该被枪杀。

这只是我找到的代码的一个小示例。

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

- - - - - - - - - - - -或者 - - - - - - - - - - -

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10

Denying the use of the GOTO statement to programmers is like telling a carpenter not to use a hammer as it Might damage the wall while he is hammering in a nail. A real programmer Knows How and When to use a GOTO. I’ve followed behind some of these so-called ‘Structured Programs’ I’ve see such Horrid code just to avoid using a GOTO, that I could shoot the programmer. Ok, In defense of the other side, I’ve seen some real spaghetti code too and again, those programmers should be shot too.

Here is just one small example of code I’ve found.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------OR----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10
笑饮青盏花 2024-07-11 10:22:40

我们已经进行了讨论,我支持我的观点

此外,我厌倦了人们将高级语言结构描述为“变相的goto”,因为他们显然根本没有明白要点。 例如:

即使是Scheme中先进的延续控制结构也可以被描述为一个复杂的goto。

这完全是无稽之谈。 每个控制结构都可以用goto来实现,但这种观察是完全微不足道且无用的。 goto 不被认为是有害的,因为它有积极的影响,但因为它有负面的后果,而这些后果已被结构化编程所消除。

同样,说“GOTO 是一个工具,并且与所有工具一样,它可以被使用和滥用”也是完全没有道理的。 现代建筑工人不会使用岩石并声称它“是一种工具”。 岩石已被锤子取代。 goto 已被控制结构取代。 如果建筑工人被困在野外,没有锤子,他当然会用石头代替。 如果程序员必须使用不具备 X 功能的劣质编程语言,那么她当然可能必须使用 goto 来代替。 但是,如果她在其他地方使用它而不是适当的语言功能,那么她显然没有正确理解该语言并且错误地使用了它。 真的就是这么简单。

We already had this discussion and I stand by my point.

Furthermore, I'm fed up with people describing higher-level language structures as “goto in disguise” because they clearly haven't got the point at all. For example:

Even the advanced continuation control structure in Scheme can be described as a sophisticated goto.

That is complete nonsense. Every control structure can be implemented in terms of goto but this observation is utterly trivial and useless. goto isn't considered harmful because of its positive effects but because of its negative consequences and these have been eliminated by structured programming.

Similarly, saying “GOTO is a tool, and as all tools, it can be used and abused” is completely off the mark. No modern construction worker would use a rock and claim it “is a tool.” Rocks have been replaced by hammers. goto has been replaced by control structures. If the construction worker were stranded in the wild without a hammer, of course he would use a rock instead. If a programmer has to use an inferior programming language that doesn't have feature X, well, of course she may have to use goto instead. But if she uses it anywhere else instead of the appropriate language feature she clearly hasn't understood the language properly and uses it wrongly. It's really as simple as that.

动次打次papapa 2024-07-11 10:22:40

如果 GOTO 本身是邪恶的,那么编译器也会是邪恶的,因为它们生成 JMP。 如果跳转到代码块(尤其是跟随指针)本质上是邪恶的,那么 RETurn 指令也将是邪恶的。 相反,邪恶在于滥用的可能性。

有时,我不得不编写必须跟踪多个对象的应用程序,其中每个对象必须遵循复杂的状态序列来响应事件,但整个事情绝对是单线程的。 如果用伪代码表示,典型的状态序列将是:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

我确信这不是新的,但我在 C(++) 中处理它的方式是定义一些宏:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

然后(假设状态最初为 0)上面的结构化状态机变成了结构化代码:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

对此进行了变体,可以有 CALL 和 RETURN,因此某些状态机可以像其他状态机的子例程一样运行。

这很不寻常吗? 是的。 维护者需要学习一些知识吗? 是的。 这种学习有回报吗? 我想是这样。 如果没有跳转到块的 GOTO 是否可以完成? 没有。

If GOTO itself were evil, compilers would be evil, because they generate JMPs. If jumping into a block of code, especially following a pointer, were inherently evil, the RETurn instruction would be evil. Rather, the evil is in the potential for abuse.

At times I have had to write apps that had to keep track of a number of objects where each object had to follow an intricate sequence of states in response to events, but the whole thing was definitely single-thread. A typical sequence of states, if represented in pseudo-code would be:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

I'm sure this is not new, but the way I handled it in C(++) was to define some macros:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Then (assuming state is initially 0) the structured state machine above turns into the structured code:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

With a variation on this, there can be CALL and RETURN, so some state machines can act like subroutines of other state machines.

Is it unusual? Yes. Does it take some learning on the part of the maintainer? Yes. Does that learning pay off? I think so. Could it be done without GOTOs that jump into blocks? Nope.

不知在何时 2024-07-11 10:22:40

在 C 和 C++(以及其他罪魁祸首)标记出中断和继续之前,goto 将继续发挥作用。

Until C and C++ (amongst other culprits) have labelled breaks and continues, goto will continue to have a role.

薄暮涼年 2024-07-11 10:22:40

在我的程序清单中,Goto 的位置非常靠后。 这并不意味着这是不可接受的。

Goto 对于状态机来说非常有用。 循环中的 switch 语句是(按照典型重要性的顺序):(a)实际上并不代表控制流,(b)丑陋,(c)根据语言和编译器可能效率低下。 因此,您最终会为每个状态编写一个函数,并执行诸如“return NEXT_STATE;”之类的操作 甚至看起来像 goto。

诚然,以易于理解的方式对状态机进行编码是很困难的。 然而,这些困难都与使用 goto 无关,并且不能通过使用替代控制结构来减少这些困难。 除非你的语言有“状态机”结构。 我的没有。

在极少数情况下,当您的算法确实最容易理解时,通过一系列节点(状态)的路径通过一组有限的允许转换(goto)连接,而不是通过任何更具体的控制流(循环、条件等) ),那么这应该在代码中明确表示。 你应该画一个漂亮的图表。

setjmp/longjmp 非常适合实现异常或类似异常的行为。 虽然没有受到普遍赞扬,但异常通常被认为是“有效”的控制结构。

setjmp/longjmp 比 goto “更危险”,因为它们更难正确使用,更不用说理解性了。

从来没有,将来也不会
永远是,任何语言
一点点难写不好
代码。 ——唐纳德·高德纳。

从 C 中去掉 goto 并不会让用 C 编写好的代码变得更容易。事实上,它宁愿忽略 C 应该能够充当一种美化的汇编语言的这一点。

接下来是“被认为有害的指针”,然后是“被认为有害的鸭子类型”。 那么当他们来夺走你不安全的编程结构时,谁会来保护你呢? 呃?

Goto is extremely low on my list of things to include in a program just for the sake of it. That doesn't mean it's unacceptable.

Goto can be nice for state machines. A switch statement in a loop is (in order of typical importance): (a) not actually representative of the control flow, (b) ugly, (c) potentially inefficient depending on language and compiler. So you end up writing one function per state, and doing things like "return NEXT_STATE;" which even look like goto.

Granted, it is difficult to code state machines in a way which make them easy to understand. However, none of that difficulty is to do with using goto, and none of it can be reduced by using alternative control structures. Unless your language has a 'state machine' construct. Mine doesn't.

On those rare occasions when your algorithm really is most comprehensible in terms of a path through a sequence of nodes (states) connected by a limited set of permissible transitions (gotos), rather than by any more specific control flow (loops, conditionals, whatnot), then that should be explicit in the code. And you ought to draw a pretty diagram.

setjmp/longjmp can be nice for implementing exceptions or exception-like behaviour. While not universally praised, exceptions are generally considered a "valid" control structure.

setjmp/longjmp are 'more dangerous' than goto in the sense that they're harder to use correctly, never mind comprehensibly.

There never has been, nor will there
ever be, any language in which it is
the least bit difficult to write bad
code. -- Donald Knuth.

Taking goto out of C would not make it any easier to write good code in C. In fact, it would rather miss the point that C is supposed to be capable of acting as a glorified assembler language.

Next it'll be "pointers considered harmful", then "duck typing considered harmful". Then who will be left to defend you when they come to take away your unsafe programming construct? Eh?

风追烟花雨 2024-07-11 10:22:40

我只记得使用过一次 goto。 我有一系列五个嵌套计数循环,我需要能够根据某些条件尽早从内部突破整个结构:

    for{
      for{
        for{
          for{
            for{
              if(stuff){
                GOTO ENDOFLOOPS;
              }
            }
          }
        }
      }
    }
    
    ENDOFLOOPS:

我可以轻松声明一个布尔中断变量并将其用作条件的一部分每个循环,但在这种情况下,我认为 GOTO 同样实用且可读。

没有迅猛龙攻击我。

I can only recall using a goto once. I had a series of five nested counted loops and I needed to be able to break out of the entire structure from the inside early based on certain conditions:

    for{
      for{
        for{
          for{
            for{
              if(stuff){
                GOTO ENDOFLOOPS;
              }
            }
          }
        }
      }
    }
    
    ENDOFLOOPS:

I could just have easily declared a boolean break variable and used it as part of the conditional for each loop, but in this instance I decided a GOTO was just as practical and just as readable.

No velociraptors attacked me.

迷雾森÷林ヴ 2024-07-11 10:22:40

有时,使用 GOTO 作为单个函数中异常处理的替代方法是有效的:

    if (f() == false) goto err_cleanup;
    if (g() == false) goto err_cleanup;
    if (h() == false) goto err_cleanup;
    
    return;
    
    err_cleanup:
    ...

COM 代码似乎经常陷入这种模式。

Sometimes it is valid to use GOTO as an alternative to exception handling within a single function:

    if (f() == false) goto err_cleanup;
    if (g() == false) goto err_cleanup;
    if (h() == false) goto err_cleanup;
    
    return;
    
    err_cleanup:
    ...

COM code seems to fall into this pattern fairly often.

酒废 2024-07-11 10:22:40

如果您正在用 C 语言编写 VM,则事实证明,使用(gcc 的)计算 goto 的速度

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

比循环内的传统 switch 快得多。

If you're writing a VM in C, it turns out that using (gcc's) computed gotos like this:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

works much faster than the conventional switch inside a loop.

离鸿 2024-07-11 10:22:40

以下陈述是概括性的; 虽然总是可以提出例外请求,但通常(以我的经验和拙见)不值得冒险。

  1. 不受限制地使用内存地址(GOTO 或原始指针)提供了太多犯容易避免的错误的机会。
  2. 到达代码中特定“位置”的方法越多,人们对此时系统状态的信心就越低。 (见下文。)
  3. 恕我直言,结构化编程并不是“避免 GOTO”,而是更多地使代码结构与数据结构相匹配。 例如,重复的数据结构(例如数组、顺序文件等)自然由重复的代码单元处理。 拥有内置结构(例如 while、for、until、for-each 等)可以让程序员避免重复相同陈词滥调的代码模式的乏味。
  4. 即使 GOTO 是低级实现细节(并非总是如此!),它也低于程序员应该考虑的级别。 有多少程序员用原始二进制来平衡他们的个人支票簿? 有多少程序员担心磁盘上的哪个扇区包含特定记录,而不是仅仅为数据库引擎提供密钥(如果我们真的根据物理磁盘扇区编写所有程序,有多少种情况可能会出错)?

上述脚注:

关于第 2 点,请考虑以下代码:

    a = b + 1
    /* do something with a */

在代码中的“做某事”点,我们可以高度自信地声明 a 大于 b。 (是的,我忽略了未捕获整数溢出的可能性。我们不要陷入一个简单的例子。)

另一方面,如果代码是这样读的:

    ...
    goto 10
    ...
    a = b + 1
    10: /* do something with a */
    ...
    goto 10
    ...

到达标签 10 的方法有多种,这意味着我们必须更加努力地对 ab 之间的关系充满信心。 (事实上​​,在一般情况下,它是不可判定的!)

关于第 4 点,代码中“去某个地方”的整个概念只是一个隐喻。 除了电子和光子(用于废热)之外,没有任何东西真正“移动”到 CPU 内部的任何地方。 有时我们会放弃另一种更有用的隐喻。 我记得遇到过(几十年前!)一种

    if (some condition) {
      action-1
    } else {
      action-2
    }

在虚拟机上实现的语言,方法是将action-1和action-2编译为外线无参数例程,然后使用单个双参数VM操作码,该操作码使用布尔值调用其中之一的条件的值。 这个概念只是“选择现在调用什么”而不是“去这里或去那里”。 再说一遍,只是比喻的改变。

The following statements are generalizations; while it is always possible to plead exception, it usually (in my experience and humble opinion) isn't worth the risks.

  1. Unconstrained use of memory addresses (either GOTO or raw pointers) provides too many opportunities to make easily avoidable mistakes.
  2. The more ways there are to arrive at a particular "location" in the code, the less confident one can be about what the state of the system is at that point. (See below.)
  3. Structured programming IMHO is less about "avoiding GOTOs" and more about making the structure of the code match the structure of the data. For example, a repeating data structure (e.g. array, sequential file, etc.) is naturally processed by a repeated unit of code. Having built-in structures (e.g. while, for, until, for-each, etc.) allows the programmer to avoid the tedium of repeating the same cliched code patterns.
  4. Even if GOTO is low-level implementation detail (not always the case!) it's below the level that the programmer should be thinking. How many programmers balance their personal checkbooks in raw binary? How many programmers worry about which sector on the disk contains a particular record, instead of just providing a key to a database engine (and how many ways could things go wrong if we really wrote all of our programs in terms of physical disk sectors)?

Footnotes to the above:

Regarding point 2, consider the following code:

    a = b + 1
    /* do something with a */

At the "do something" point in the code, we can state with high confidence that a is greater than b. (Yes, I'm ignoring the possibility of untrapped integer overflow. Let's not bog down a simple example.)

On the other hand, if the code had read this way:

    ...
    goto 10
    ...
    a = b + 1
    10: /* do something with a */
    ...
    goto 10
    ...

The multiplicity of ways to get to label 10 means that we have to work much harder to be confident about the relationships between a and b at that point. (In fact, in the general case it's undecideable!)

Regarding point 4, the whole notion of "going someplace" in the code is just a metaphor. Nothing is really "going" anywhere inside the CPU except electrons and photons (for the waste heat). Sometimes we give up a metaphor for another, more useful, one. I recall encountering (a few decades ago!) a language where

    if (some condition) {
      action-1
    } else {
      action-2
    }

was implemented on a virtual machine by compiling action-1 and action-2 as out-of-line parameterless routines, then using a single two-argument VM opcode which used the boolean value of the condition to invoke one or the other. The concept was simply "choose what to invoke now" rather than "go here or go there". Again, just a change of metaphor.

表情可笑 2024-07-11 10:22:40

只要你能够独立思考,就永远不会。

It never was, as long as you were able to think for yourself.

夕嗳→ 2024-07-11 10:22:40

因为 goto 可用于混淆元编程,所以

Goto 既是高级控制表达式,又是低级控制表达式,因此它没有适合大多数问题的适当设计模式。

从某种意义上说,它是低级,因为 goto 是一种原始操作,它实现了 whileforeach 等更高级别的操作。

它是高级,因为当以某些方式使用时,它采用以清晰的顺序、不间断的方式执行的代码(结构化循环除外),并将其更改为逻辑片段,有足够的goto,动态重组的逻辑的抓包。

因此,goto平淡邪恶的一面。

平淡无奇的一面是,向上的 goto 可以实现完全合理的循环,而向下的 goto 可以实现完全合理的 breakreturn 。 当然,实际的 whilebreakreturn 会更具可读性,因为可怜的人不必模拟goto 的效果以获得全局。 所以,总的来说,这是一个坏主意。

邪恶的一面涉及一个例程,不使用 goto 来执行 while、break 或 return,而是将其用于所谓的意大利面条逻辑。 在这种情况下,喜欢 goto 的开发人员正在从 goto 的迷宫中构建代码片段,理解它的唯一方法是在心理上模拟它作为一个整体,当有很多 goto 时,这是一项非常累人的任务。 我的意思是,想象一下评估代码时的麻烦,其中 else 并不完全是 if 的逆,其中嵌套的 if 可能允许在某些情况下使用 语句

最后,为了真正涵盖这个主题,我们应该注意到,基本上除了 Algol 之外的所有早期语言最初只做出了受其 if-then-else。 因此,执行条件块的唯一方法是使用逆条件goto来绕过它。 我知道这很疯狂,但我读过一些旧规格。 请记住,第一台计算机是用二进制机器代码编程的,因此我认为任何类型的 HLL 都是救星; 我想他们对于自己获得的 HLL 功能并不太挑剔。

说了这么多,我曾经在我编写的每个程序中都插入了一个 goto,“只是为了惹恼纯粹主义者”。

Because goto can be used for confusing metaprogramming

Goto is both a high-level and a low-level control expression, and as a result it just doesn't have a appropriate design pattern suitable for most problems.

It's low-level in the sense that a goto is a primitive operation that implements something higher like while or foreach or something.

It's high-level in the sense that when used in certain ways it takes code that executes in a clear sequence, in an uninterrupted fashion, except for structured loops, and it changes it into pieces of logic that are, with enough gotos, a grab-bag of logic being dynamically reassembled.

So, there is a prosaic and an evil side to goto.

The prosaic side is that an upward pointing goto can implement a perfectly reasonable loop and a downward-pointing goto can do a perfectly reasonable break or return. Of course, an actual while, break, or return would be a lot more readable, as the poor human wouldn't have to simulate the effect of the goto in order to get the big picture. So, a bad idea in general.

The evil side involves a routine not using goto for while, break, or return, but using it for what's called spaghetti logic. In this case the goto-happy developer is constructing pieces of code out of a maze of goto's, and the only way to understand it is to simulate it mentally as a whole, a terribly tiring task when there are many goto's. I mean, imagine the trouble of evaluating code where the else is not precisely an inverse of the if, where nested ifs might allow in some things that were rejected by the outer if, etc, etc.

Finally, to really cover the subject, we should note that essentially all early languages except Algol initially made only single statements subject to their versions of if-then-else. So, the only way to do a conditional block was to goto around it using an inverse conditional. Insane, I know, but I've read some old specs. Remember that the first computers were programmed in binary machine code so I suppose any kind of an HLL was a lifesaver; I guess they weren't too picky about exactly what HLL features they got.

Having said all that I used to stick one goto into every program I wrote "just to annoy the purists".

别靠近我心 2024-07-11 10:22:40

自从我开始在 Linux 内核中做一些事情以来,goto 不再像以前那样困扰我了。 起初,我有点震惊地看到他们(内核人员)在我的代码中添加了 goto。 从那时起,我已经习惯了在某些有限的上下文中使用 goto,现在我自己也会偶尔使用它们。 通常,它是跳转到函数末尾以执行某种清理和救助的 goto,而不是在函数中的多个位置重复相同的清理和救助。 通常情况下,它的大小不足以传递给另一个函数——例如,释放一些本地 (k)malloc 的变量是一个典型的情况。

我编写的代码只使用了一次 setjmp/longjmp 。 它位于 MIDI 鼓音序器程序中。 播放发生在与所有用户交互无关的单独进程中,并且播放进程使用与 UI 进程共享的内存来获取播放所需的有限信息。 当用户想要停止播放时,播放过程只是执行一个 longjmp“回到开头”来重新开始,而不是在用户希望停止时对它恰好正在执行的位置进行一些复杂的展开。 它工作得很好,很简单,在那种情况下我从未遇到过任何与之相关的问题或错误。

setjmp/longjmp 有它们的位置——但那个地方你不太可能访问,但很长一段时间才会访问一次。

编辑:我刚刚看了代码。 实际上我使用的是 siglongjmp(),而不是 longjmp(并不是说这有什么大不了的,但我忘记了 siglongjmp 甚至存在。)

Since I began doing a few things in the linux kernel, gotos don't bother me so much as they once did. At first I was sort of horrified to see they (kernel guys) added gotos into my code. I've since become accustomed to the use of gotos, in some limited contexts, and will now occasionally use them myself. Typically, it's a goto that jumps to the end of a function to do some kind of cleanup and bail out, rather than duplicating that same cleanup and bailout in several places in the function. And typically, it's not something large enough to hand off to another function -- e.g. freeing some locally (k)malloc'ed variables is a typical case.

I've written code that used setjmp/longjmp only once. It was in a MIDI drum sequencer program. Playback happened in a separate process from all user interaction, and the playback process used shared memory with the UI process to get the limited info it needed to do the playback. When the user wanted to stop playback, the playback process just did a longjmp "back to the beginning" to start over, rather than some complicated unwinding of wherever it happened to be executing when the user wanted it to stop. It worked great, was simple, and I never had any problems or bugs related to it in that instance.

setjmp/longjmp have their place -- but that place is one you'll not likely visit but once in a very long while.

Edit: I just looked at the code. It was actually siglongjmp() that I used, not longjmp (not that it's a big deal, but I had forgotten that siglongjmp even existed.)

青芜 2024-07-11 10:22:40

不存在GOTO 被认为有害的事情

GOTO 是一个工具,并且与所有工具一样,它可以被使用和滥用。

然而,编程世界中的许多工具往往被滥用多于使用,GOTO 就是其中之一。 Delphi 的 WITH 语句是另一个。

就我个人而言,我在典型代码中不使用任何一个,但我有过 GOTO 和WITH 的奇怪用法,这是有保证的,并且另一种解决方案将包含更多代码。

最好的解决方案是编译器只是警告您关键字被污染,并且您必须在语句周围填充一些杂注指令以消除警告。

这就像告诉您的孩子不要拿着剪刀奔跑。 剪刀不错,但使用它们可能并不是保持健康的最佳方法。

There is no such things as GOTO considered harmful.

GOTO is a tool, and as all tools, it can be used and abused.

There are, however, many tools in the programming world that have a tendency to be abused more than being used, and GOTO is one of them. the WITH statement of Delphi is another.

Personally I don't use either in typical code, but I've had the odd usage of both GOTO and WITH that were warranted, and an alternative solution would've contained more code.

The best solution would be for the compiler to just warn you that the keyword was tainted, and you'd have to stuff a couple of pragma directives around the statement to get rid of the warnings.

It's like telling your kids to not run with scissors. Scissors are not bad, but some usage of them are perhaps not the best way to keep your health.

幻梦 2024-07-11 10:22:40

今天,很难看出 GOTO 语句有什么大不了的,因为“结构化编程”人们大多赢得了这场辩论,而且今天的语言有足够的控制流结构来避免 GOTO

计算现代 C 程序中 goto 的数量。 现在添加 breakcontinuereturn 语句的数量。 此外,添加使用 ifelsewhileswitchcase< 的次数/代码>。 这大约是如果您在 1968 年 Dijkstra 写信时使用 FORTRAN 或 BASIC 编写程序,那么您的程序将有多少个 GOTO

当时的编程语言缺乏控制流。 例如,在最初的 Dartmouth BASIC 中:

  • IF 语句没有 ELSE。 如果你想要一个,你必须写:

    100 IF NOT 条件 THEN GOTO 200 
      ...如果条件为真则要做的事情... 
      190 转到 300 
      200 REM 其他 
      ...如果条件为假则要做的事情... 
      300 REM 结束如果 
      
  • 即使您的 IF 语句不需要 ELSE,它仍然限于一行,这通常由 GOTO 组成。

  • 没有 DO...LOOP 语句。 对于非 FOR 循环,您必须使用显式 GOTOIF...GOTO 结束循环,回到开头。

  • 没有SELECT CASE。 您必须使用ON...GOTO

因此,您的程序中最终出现了很多GOTO。 并且您不能依赖于单个子例程中的 GOTO 限制(因为 GOSUB...RETURN 是子例程的一个弱概念),因此这些 < code>GOTO 可以去任何地方。 显然,这使得控制流程难以遵循。

这就是反GOTO运动的由来。

Today, it's hard to see the big deal about the GOTO statement because the "structured programming" people mostly won the debate and today's languages have sufficient control flow structures to avoid GOTO.

Count the number of gotos in a modern C program. Now add the number of break, continue, and return statements. Furthermore, add the number of times you use if, else, while, switch or case. That's about how many GOTOs your program would have had if you were writing in FORTRAN or BASIC in 1968 when Dijkstra wrote his letter.

Programming languages at the time were lacking in control flow. For example, in the original Dartmouth BASIC:

  • IF statements had no ELSE. If you wanted one, you had to write:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • Even if your IF statement didn't need an ELSE, it was still limited to a single line, which usually consisted of a GOTO.

  • There was no DO...LOOP statement. For non-FOR loops, you had to end the loop with an explicit GOTO or IF...GOTO back to the beginning.

  • There was no SELECT CASE. You had to use ON...GOTO.

So, you ended up with a lot of GOTOs in your program. And you couldn't depend on the restriction of GOTOs to within a single subroutine (because GOSUB...RETURN was such a weak concept of subroutines), so these GOTOs could go anywhere. Obviously, this made control flow hard to follow.

This is where the anti-GOTO movement came from.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文